@nalvietnam/avatar-cli 1.1.1 → 1.1.2

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
@@ -701,11 +701,22 @@ function installGhCliViaPackageManager(pm) {
701
701
  log.success("\u0110\xE3 c\xE0i gh CLI");
702
702
  }
703
703
 
704
- // src/lib/trigger-gh-cli-auth-login.ts
704
+ // src/lib/setup-git-credential-via-gh.ts
705
705
  import { spawnSync as spawnSync7 } from "child_process";
706
+ function setupGitCredentialViaGh() {
707
+ const r = spawnSync7("gh", ["auth", "setup-git"], { stdio: "ignore" });
708
+ if (r.status !== 0) {
709
+ log.warn("gh auth setup-git fail (non-fatal). N\u1EBFu git clone l\u1ED7i 128 \u2192 ch\u1EA1y th\u1EE7 c\xF4ng.");
710
+ return;
711
+ }
712
+ log.dim("Git credential helper \u0111\xE3 link v\u1EDBi gh token.");
713
+ }
714
+
715
+ // src/lib/trigger-gh-cli-auth-login.ts
716
+ import { spawnSync as spawnSync8 } from "child_process";
706
717
  function triggerGhCliAuthLogin() {
707
718
  log.info("Kh\u1EDFi \u0111\u1ED9ng \u0111\u0103ng nh\u1EADp GitHub qua gh CLI (browser s\u1EBD m\u1EDF)...");
708
- const r = spawnSync7(
719
+ const r = spawnSync8(
709
720
  "gh",
710
721
  ["auth", "login", "--hostname", "github.com", "--web", "--git-protocol", "ssh"],
711
722
  { stdio: "inherit" }
@@ -717,7 +728,7 @@ function triggerGhCliAuthLogin() {
717
728
  }
718
729
 
719
730
  // src/lib/verify-git-remote-accessible.ts
720
- import { spawnSync as spawnSync8 } from "child_process";
731
+ import { spawnSync as spawnSync9 } from "child_process";
721
732
  var TIMEOUT_MS = 5e3;
722
733
  var RemoteNotAccessibleError = class extends Error {
723
734
  constructor(url, reason) {
@@ -726,7 +737,7 @@ var RemoteNotAccessibleError = class extends Error {
726
737
  }
727
738
  };
728
739
  function verifyGitRemoteAccessible(url) {
729
- const r = spawnSync8("git", ["ls-remote", "--exit-code", url, "HEAD"], {
740
+ const r = spawnSync9("git", ["ls-remote", "--exit-code", url, "HEAD"], {
730
741
  stdio: "ignore",
731
742
  timeout: TIMEOUT_MS
732
743
  });
@@ -758,6 +769,7 @@ async function ensureGitHubReady(remoteUrl) {
758
769
  }
759
770
  }
760
771
  log.success("gh CLI s\u1EB5n s\xE0ng");
772
+ setupGitCredentialViaGh();
761
773
  if (remoteUrl) {
762
774
  verifyGitRemoteAccessible(remoteUrl);
763
775
  log.success(`Remote accessible: ${remoteUrl}`);
@@ -1686,7 +1698,7 @@ async function removeSubmoduleEntry(gitmodulesPath, submodulePath) {
1686
1698
  }
1687
1699
 
1688
1700
  // src/commands/uninstall.ts
1689
- var CLI_VERSION = "1.1.1";
1701
+ var CLI_VERSION = "1.1.2";
1690
1702
  function registerUninstallCommand(program2) {
1691
1703
  program2.command("uninstall").description("G\u1EE1 Avatar kh\u1ECFi project \u2014 backup t\u1EF1 \u0111\u1ED9ng (M11)").option("--yes", "Skip confirm prompt").option("--no-backup", "Kh\xF4ng t\u1EA1o backup tr\u01B0\u1EDBc khi x\xF3a (nguy hi\u1EC3m)").option("--keep-submodule", "Gi\u1EEF submodule .claude/pack/").option("--keep-hooks", "Gi\u1EEF git hooks post-merge, pre-push").option("--dry-run", "Hi\u1EC3n th\u1ECB danh s\xE1ch s\u1EBD x\xF3a, kh\xF4ng th\u1EF1c thi").action(async (opts) => {
1692
1704
  try {
@@ -1768,7 +1780,7 @@ function printUninstallSuccessBox(backupPath) {
1768
1780
  }
1769
1781
 
1770
1782
  // src/index.ts
1771
- var CLI_VERSION2 = "1.1.1";
1783
+ var CLI_VERSION2 = "1.1.2";
1772
1784
  var program = new Command();
1773
1785
  program.name("avatar").description("AI harness CLI for NAL Vietnam engineering").version(CLI_VERSION2, "-v, --version", "Hi\u1EC3n th\u1ECB phi\xEAn b\u1EA3n Avatar CLI").addHelpText(
1774
1786
  "beforeAll",
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/avatar-ascii-banner.ts","../src/lib/execute-gh-repo-create.ts","../src/lib/resolve-github-username-default.ts","../src/lib/validate-repo-name-and-visibility.ts","../src/lib/create-github-remote-from-folder.ts","../src/lib/check-gh-cli-auth-status.ts","../src/lib/detect-package-manager.ts","../src/lib/detect-host-platform.ts","../src/lib/install-gh-cli-via-package-manager.ts","../src/lib/trigger-gh-cli-auth-login.ts","../src/lib/verify-git-remote-accessible.ts","../src/lib/git-auth-and-install-orchestrator.ts","../src/lib/check-folder-has-git.ts","../src/lib/create-initial-git-commit.ts","../src/lib/detect-folder-tech-stack.ts","../src/lib/gitignore-template-loader.ts","../src/lib/write-or-merge-gitignore.ts","../src/lib/git-bootstrap-orchestrator.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","../src/commands/uninstall.ts","../src/lib/create-uninstall-backup-snapshot.ts","../src/lib/detect-avatar-project-artifacts.ts","../src/lib/execute-uninstall-deletion.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\";\nimport { registerUninstallCommand } from \"./commands/uninstall.js\";\nimport { printAvatarBanner, renderAvatarBanner } from \"./lib/avatar-ascii-banner.js\";\n\nconst CLI_VERSION = \"1.1.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 // Override mặc định của commander để in banner kèm version. Commander cho phép\n // custom output qua configureOutput nhưng cách đơn giản nhất là intercept ở argv.\n .addHelpText(\n \"beforeAll\",\n () =>\n `\\n${renderAvatarBanner({ tagline: `v${CLI_VERSION} · AI harness CLI for NAL Vietnam` })}\\n\\n`,\n );\n\n// In banner khi user gọi `avatar -v` / `avatar --version`. Banner đã chứa\n// version trong tagline nên ta short-circuit luôn, không để commander in thêm\n// dòng \"1.0.1\" dư thừa.\nconst isVersionCall = process.argv.includes(\"-v\") || process.argv.includes(\"--version\");\nif (isVersionCall) {\n printAvatarBanner({ tagline: `v${CLI_VERSION} · AI harness CLI for NAL Vietnam` });\n process.exit(0);\n}\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);\nregisterUninstallCommand(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 (v1.1 redesign).\n//\n// Bỏ khái niệm --mode internal/client/library. Thay bằng wizard 3-câu hỏi tự\n// nhận diện tình trạng dự án:\n// 1. Đã có repo git remote (URL có sẵn) → flow=existing-remote\n// 2. Đã có folder code local → flow=existing-folder\n// 3. Dự án mới hoàn toàn → flow=new-project\n//\n// Mọi flow đều scaffold workspace tách biệt (không còn mode internal). Avatar\n// luôn check + tự cài gh CLI nếu thiếu, auto-bootstrap git cho folder chưa\n// init, auto-detect .gitignore theo tech stack.\n\nimport { basename, 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 { printAvatarBanner } from \"../lib/avatar-ascii-banner.js\";\nimport { checkFolderHasGit } from \"../lib/check-folder-has-git.js\";\nimport { createGithubRemoteFromFolder } from \"../lib/create-github-remote-from-folder.js\";\nimport { ensureDir } from \"../lib/filesystem-helpers.js\";\nimport { ensureGitHubReady } from \"../lib/git-auth-and-install-orchestrator.js\";\nimport { bootstrapGitInFolder } from \"../lib/git-bootstrap-orchestrator.js\";\nimport { git } 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 { 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 { RepoVisibility } from \"../lib/validate-repo-name-and-visibility.js\";\nimport {\n findAlternativeWorkspaceName,\n isEmptyOrMissing,\n} from \"./init-conflict-detection-helpers.js\";\nimport { buildScaffoldVariables, inferWorkspaceName } from \"./init-scaffold-variable-builders.js\";\n\n// 3 flow values + deprecated legacy \"mode\" alias.\ntype ProjectStatus = \"existing-remote\" | \"existing-folder\" | \"new-project\";\n\ninterface InitOptions {\n // New v1.1 flags\n projectStatus?: ProjectStatus;\n folderPath?: string;\n createRemote?: boolean;\n repoVisibility?: string;\n repoOrg?: string;\n // Carried over\n skipScan?: boolean;\n packVersion?: string;\n clientRepo?: string; // reused: URL khi projectStatus=existing-remote\n workspaceName?: string;\n workspaceParent?: string;\n force?: boolean;\n teamOwner?: string;\n description?: string;\n yes?: boolean;\n // Legacy (deprecated)\n mode?: string;\n}\n\nexport function registerInitCommand(program: Command): void {\n program\n .command(\"init\")\n .description(\"Khởi tạo Avatar — 3 flow tự nhận diện (repo / folder / new)\")\n .option(\"--project-status <val>\", \"existing-remote | existing-folder | new-project\")\n .option(\"--folder-path <path>\", \"Đường dẫn folder hiện có (flow existing-folder)\")\n .option(\"--create-remote\", \"Force tạo remote qua gh (flow existing-folder hoặc new-project)\")\n .option(\"--repo-visibility <val>\", \"private (mặc định) | public\")\n .option(\"--repo-org <name>\", \"GitHub org/owner cho repo mới\")\n .option(\"--client-repo <url>\", \"URL git remote (flow existing-remote)\")\n .option(\"--workspace-name <name>\", \"Tên workspace\")\n .option(\"--workspace-parent <path>\", \"Thư mục cha tạo workspace (mặc định ..)\")\n .option(\"--pack-version <tag>\", \"Pin team-ai-pack vào tag cụ thể\")\n .option(\"--team-owner <email>\", \"Email team owner (bỏ qua prompt)\")\n .option(\"--description <text>\", \"Mô tả 1 dòng của dự án\")\n .option(\"--skip-scan\", \"Bỏ qua project-scanner sau scaffold\")\n .option(\"--force\", \"Bỏ qua prompt khi workspace path đã tồn tại\")\n .option(\"--yes\", \"Auto-confirm tất cả prompt\")\n .option(\"--mode <mode>\", \"[DEPRECATED] Dùng --project-status thay thế\")\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 if (!opts.yes) printAvatarBanner({ tagline: \"Khởi tạo Avatar trong dự án của bạn\" });\n\n if (opts.mode) {\n log.warn(\"Flag --mode đã deprecated từ v1.1. Dùng --project-status thay thế.\");\n }\n\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 const status = opts.projectStatus ?? (await promptProjectStatus());\n\n switch (status) {\n case \"existing-remote\":\n await runInitFromExistingRemote(opts, userConfig.email);\n break;\n case \"existing-folder\":\n await runInitFromExistingFolder(opts, userConfig.email);\n break;\n case \"new-project\":\n await runInitFromScratch(opts, userConfig.email);\n break;\n }\n}\n\nasync function promptProjectStatus(): Promise<ProjectStatus> {\n return (await select({\n message: \"Tình trạng dự án của bạn?\",\n choices: [\n { name: \"1. Đã có repo git remote (URL có sẵn)\", value: \"existing-remote\" as const },\n { name: \"2. Đã có folder code local\", value: \"existing-folder\" as const },\n { name: \"3. Dự án mới hoàn toàn\", value: \"new-project\" as const },\n ],\n })) as ProjectStatus;\n}\n\n// ─── FLOW 1: EXISTING REMOTE ────────────────────────────────────────────────\nasync function runInitFromExistingRemote(opts: InitOptions, ownerEmail: string): Promise<void> {\n const remoteUrl =\n opts.clientRepo ??\n (await input({\n message: \"URL git của repo:\",\n validate: (v) => (v.length > 0 ? true : \"URL bắt buộc\"),\n }));\n\n await ensureGitHubReady(remoteUrl);\n\n const teamOwner = opts.teamOwner ?? (await promptTeamOwner(ownerEmail));\n const inferredName = inferWorkspaceName(remoteUrl);\n const workspaceName =\n opts.workspaceName ?? (await input({ message: \"Tên workspace:\", default: inferredName }));\n const workspaceParent = resolve(opts.workspaceParent ?? \"..\");\n const workspacePath = await resolveWorkspacePath(workspaceParent, workspaceName, opts.force);\n\n await scaffoldWorkspaceWithSrcSubmodule({\n workspacePath,\n workspaceName,\n srcRemoteUrl: remoteUrl,\n teamOwner,\n description: opts.description ?? `Avatar workspace cho ${remoteUrl}`,\n packVersion: opts.packVersion,\n autoYes: opts.yes,\n flow: \"existing-remote\",\n });\n}\n\n// ─── FLOW 2: EXISTING FOLDER ────────────────────────────────────────────────\nasync function runInitFromExistingFolder(opts: InitOptions, ownerEmail: string): Promise<void> {\n const folderPath = resolve(\n opts.folderPath ??\n (await input({\n message: \"Đường dẫn folder hiện có:\",\n validate: (v) => (v.length > 0 ? true : \"Path bắt buộc\"),\n })),\n );\n\n // Bootstrap git nếu chưa có (đồng thời ghi .gitignore Avatar block).\n await bootstrapGitInFolder(folderPath);\n\n // Check remote origin. Có → dùng luôn. Chưa có → hỏi tạo mới (default Có).\n const remoteUrl = await getOrCreateOriginRemote(folderPath, opts);\n\n const teamOwner = opts.teamOwner ?? (await promptTeamOwner(ownerEmail));\n const inferredName = opts.workspaceName ?? `${basename(folderPath)}-avatar-workspace`;\n const workspaceName =\n opts.workspaceName ?? (await input({ message: \"Tên workspace:\", default: inferredName }));\n const workspaceParent = resolve(opts.workspaceParent ?? \"..\");\n const workspacePath = await resolveWorkspacePath(workspaceParent, workspaceName, opts.force);\n\n await scaffoldWorkspaceWithSrcSubmodule({\n workspacePath,\n workspaceName,\n srcRemoteUrl: remoteUrl ?? folderPath, // fallback local path nếu user từ chối tạo remote\n teamOwner,\n description: opts.description ?? `Avatar workspace cho folder ${folderPath}`,\n packVersion: opts.packVersion,\n autoYes: opts.yes,\n flow: \"existing-folder\",\n });\n}\n\n// ─── FLOW 3: NEW PROJECT ────────────────────────────────────────────────────\nasync function runInitFromScratch(opts: InitOptions, ownerEmail: string): Promise<void> {\n await ensureGitHubReady();\n\n const projectName =\n opts.workspaceName ??\n (await input({\n message: \"Tên dự án:\",\n validate: (v) => (v.length > 0 ? true : \"Tên bắt buộc\"),\n }));\n const visibility = (opts.repoVisibility ??\n (await select({\n message: \"Visibility?\",\n choices: [\n { name: \"private (mặc định)\", value: \"private\" as const },\n { name: \"public\", value: \"public\" as const },\n ],\n }))) as RepoVisibility;\n\n const teamOwner = opts.teamOwner ?? (await promptTeamOwner(ownerEmail));\n const workspaceParent = resolve(opts.workspaceParent ?? \".\");\n const workspacePath = await resolveWorkspacePath(workspaceParent, projectName, opts.force);\n const srcPath = join(workspacePath, \"src\");\n\n // Tạo workspace dir + src/ rỗng + bootstrap git trong src/.\n await ensureDir(workspacePath);\n await ensureDir(srcPath);\n await bootstrapGitInFolder(srcPath);\n\n // Tạo remote GitHub + push initial commit.\n const urls = createGithubRemoteFromFolder({\n folder: srcPath,\n name: projectName,\n visibility,\n org: opts.repoOrg,\n });\n\n // Workspace setup: init git, submodule add cho src/.\n await git(workspacePath).init();\n const sp = spinner(\"Add submodule src/ + team-ai-pack...\");\n try {\n await git(workspacePath).subModule([\"add\", urls.sshUrl, \"src\"]);\n const result = await addTeamPackSubmodule(workspacePath, opts.packVersion);\n sp.succeed(`Pin team-ai-pack vào ${result.pinnedTag ?? \"HEAD\"}`);\n await finalizeWorkspaceScaffold({\n workspacePath,\n workspaceName: projectName,\n teamOwner,\n description: opts.description ?? `Dự án mới: ${projectName}`,\n packVersion: result.pinnedTag ?? \"HEAD\",\n autoYes: opts.yes,\n flow: \"new-project\",\n });\n } catch (err) {\n sp.fail(\"Init workspace thất bại\");\n throw err;\n }\n}\n\n// ─── HELPERS ────────────────────────────────────────────────────────────────\n\n// Check origin remote của folder; nếu chưa có thì hỏi tạo (default Có) → gh repo create.\n// Trả về URL remote nếu có, undefined nếu user từ chối (caller fallback local path).\nasync function getOrCreateOriginRemote(\n folderPath: string,\n opts: InitOptions,\n): Promise<string | undefined> {\n const remotes = await git(folderPath).getRemotes(true);\n const origin = remotes.find((r) => r.name === \"origin\");\n if (origin?.refs.push) {\n log.success(`Folder đã có remote origin: ${origin.refs.push}`);\n return origin.refs.push;\n }\n\n const shouldCreate =\n opts.createRemote ??\n (await confirm({\n message: \"Folder chưa có remote. Tạo GitHub repo ngay để share team?\",\n default: true,\n }));\n if (!shouldCreate) {\n log.warn(\"Tiếp tục với local path. Workspace chỉ chạy được trên máy bạn.\");\n return undefined;\n }\n\n await ensureGitHubReady();\n const visibility = (opts.repoVisibility ??\n (await select({\n message: \"Visibility?\",\n choices: [\n { name: \"private (mặc định)\", value: \"private\" as const },\n { name: \"public\", value: \"public\" as const },\n ],\n }))) as RepoVisibility;\n const repoName = await input({\n message: \"Tên repo:\",\n default: basename(folderPath),\n });\n const urls = createGithubRemoteFromFolder({\n folder: folderPath,\n name: repoName,\n visibility,\n org: opts.repoOrg,\n });\n return urls.sshUrl;\n}\n\n// Scaffold workspace dùng cho flow 1 + flow 2: workspace = parent của src/.\n// src/ là submodule trỏ tới remote URL hoặc local path.\nasync function scaffoldWorkspaceWithSrcSubmodule(args: {\n workspacePath: string;\n workspaceName: string;\n srcRemoteUrl: string;\n teamOwner: string;\n description: string;\n packVersion?: string;\n autoYes?: boolean;\n flow: ProjectStatus;\n}): Promise<void> {\n await ensureDir(args.workspacePath);\n await git(args.workspacePath).init();\n\n const sp = spinner(\"Add submodule src/ + team-ai-pack...\");\n try {\n await git(args.workspacePath).subModule([\"add\", args.srcRemoteUrl, \"src\"]);\n const result = await addTeamPackSubmodule(args.workspacePath, args.packVersion);\n sp.succeed(`Pin team-ai-pack vào ${result.pinnedTag ?? \"HEAD\"}`);\n\n await finalizeWorkspaceScaffold({\n workspacePath: args.workspacePath,\n workspaceName: args.workspaceName,\n teamOwner: args.teamOwner,\n description: args.description,\n packVersion: result.pinnedTag ?? \"HEAD\",\n autoYes: args.autoYes,\n flow: args.flow,\n });\n } catch (err) {\n sp.fail(\"Init workspace thất bại\");\n throw err;\n }\n}\n\n// Common scaffold step sau khi src/ + team-ai-pack đã add xong.\nasync function finalizeWorkspaceScaffold(args: {\n workspacePath: string;\n workspaceName: string;\n teamOwner: string;\n description: string;\n packVersion: string;\n autoYes?: boolean;\n flow: ProjectStatus;\n}): Promise<void> {\n // Mode \"client\" được dùng trong scaffold-variable builder cũ — giữ giá trị\n // này để compatible với template hiện tại. Sẽ rename trong release sau.\n const vars = buildScaffoldVariables({\n projectName: args.workspaceName,\n projectDescription: args.description,\n teamOwner: args.teamOwner,\n packVersion: args.packVersion,\n mode: \"client\",\n });\n\n await createClaudeDirTree(args.workspacePath);\n await writeProjectKnowledgeFiles(args.workspacePath, vars);\n await writeRootClaudeMd(args.workspacePath, vars);\n await writeProjectSettings(args.workspacePath, vars);\n await appendGitignoreEntries(args.workspacePath);\n await ensureDir(join(args.workspacePath, \"notes\"));\n await ensureDir(join(args.workspacePath, \"scripts\"));\n\n await installGitHook(join(args.workspacePath, \".git\"), \"post-merge\");\n await installGitHook(join(args.workspacePath, \".git\", \"modules\", \"src\"), \"pre-push\");\n log.success(\"Cài post-merge (workspace) + pre-push (src/)\");\n\n await appendAuditEntry(\"init\", `flow=${args.flow},workspace=${args.workspaceName}`);\n await maybeCommitWorkspace(args.workspacePath, args.autoYes);\n printInitSuccessBox(args.workspacePath, args.flow);\n}\n\n// ─── shared utilities (giữ từ v1.0.1, chỉ đổi signature mode → flow) ───────\n\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({ message: `Dùng \"${alternative}\" thay thế?`, default: true });\n if (!useAlt) throw new Error(\"Hủy init. Chạy lại với --workspace-name khác.\");\n return alternative;\n}\n\nasync function promptTeamOwner(currentUserEmail: string): Promise<string> {\n return await input({ message: \"Team owner email:\", default: currentUserEmail });\n}\n\nasync function maybeCommitWorkspace(workspacePath: string, autoYes?: boolean): Promise<void> {\n const wantCommit =\n autoYes ?? (await confirm({ message: \"Commit workspace ngay?\", default: true }));\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\");\n log.success(\"Đã commit workspace\");\n}\n\nfunction printInitSuccessBox(rootPath: string, flow: ProjectStatus): void {\n const lines: string[] = [\n `${chalk.green(\"✓\")} Workspace sẵn sàng: ${relative(process.cwd(), rootPath) || rootPath}`,\n ` ${chalk.dim(`(flow: ${flow})`)}`,\n \"\",\n ` ${chalk.cyan(`cd ${rootPath}`)}`,\n ` ${chalk.cyan(\"claude\")} Mở Claude Code ở workspace root`,\n \"\",\n ` ${chalk.cyan(\"avatar commit --src\")} Commit code lên remote src`,\n ` ${chalk.cyan(\"avatar commit --avatar\")} Commit Avatar state`,\n ` ${chalk.cyan(\"avatar sync\")} Pull team-ai-pack mới`,\n ` ${chalk.cyan(\"avatar uninstall\")} Gỡ Avatar (giữ code)`,\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","// Avatar ASCII banner — gradient màu cam → tím để in ở các entry point chính:\n// `avatar --version`, `avatar init`, `avatar login`.\n//\n// Style: ANSI Shadow block characters (Unicode box-drawing).\n// Tự fallback sang plain text khi không phải TTY (CI, pipe ra file).\nimport chalk from \"chalk\";\n\n// 6 dòng chính của logo, mỗi dòng giữ độ rộng đồng nhất 50 ký tự để gradient đẹp.\nconst BANNER_LINES: readonly string[] = [\n \" █████╗ ██╗ ██╗ █████╗ ████████╗ █████╗ ██████╗ \",\n \"██╔══██╗██║ ██║██╔══██╗╚══██╔══╝██╔══██╗██╔══██╗\",\n \"███████║██║ ██║███████║ ██║ ███████║██████╔╝\",\n \"██╔══██║╚██╗ ██╔╝██╔══██║ ██║ ██╔══██║██╔══██╗\",\n \"██║ ██║ ╚████╔╝ ██║ ██║ ██║ ██║ ██║██║ ██║\",\n \"╚═╝ ╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝\",\n] as const;\n\n// Gradient stops cam → tím (RGB triples). Mỗi dòng nội suy theo % chiều cao.\nconst GRADIENT_STOPS: ReadonlyArray<readonly [number, number, number]> = [\n [217, 79, 30], // cam-cháy (#d94f1e)\n [200, 70, 80], // cam-hồng\n [170, 70, 140], // hồng-tím\n [125, 88, 217], // tím (#7d58d9)\n];\n\n// Nội suy tuyến tính 1 kênh màu giữa 2 stop.\nfunction lerpChannel(a: number, b: number, t: number): number {\n return Math.round(a + (b - a) * t);\n}\n\n// Lấy màu RGB tại vị trí [0,1] trên gradient.\nfunction gradientAt(t: number): readonly [number, number, number] {\n const clamped = Math.max(0, Math.min(1, t));\n const scaled = clamped * (GRADIENT_STOPS.length - 1);\n const lo = Math.floor(scaled);\n const hi = Math.min(GRADIENT_STOPS.length - 1, lo + 1);\n const localT = scaled - lo;\n const a = GRADIENT_STOPS[lo];\n const b = GRADIENT_STOPS[hi];\n return [\n lerpChannel(a[0], b[0], localT),\n lerpChannel(a[1], b[1], localT),\n lerpChannel(a[2], b[2], localT),\n ];\n}\n\n// Build banner string đã tô màu sẵn. Trả empty khi terminal không hỗ trợ màu.\nexport function renderAvatarBanner(opts?: { tagline?: string }): string {\n const isTty = process.stdout.isTTY ?? false;\n const supportsColor = isTty && chalk.level > 0;\n\n // Plain fallback cho CI hoặc pipe.\n if (!supportsColor) {\n return [...BANNER_LINES, ...(opts?.tagline ? [\"\", opts.tagline] : [])].join(\"\\n\");\n }\n\n const colored = BANNER_LINES.map((line, idx) => {\n const t = BANNER_LINES.length === 1 ? 0 : idx / (BANNER_LINES.length - 1);\n const [r, g, b] = gradientAt(t);\n return chalk.rgb(r, g, b).bold(line);\n });\n\n if (opts?.tagline) {\n colored.push(\"\");\n colored.push(chalk.dim(opts.tagline));\n }\n\n return colored.join(\"\\n\");\n}\n\n// Tiện ích: in banner ra stdout với newline phía trước/sau cho thoáng.\nexport function printAvatarBanner(opts?: { tagline?: string }): void {\n process.stdout.write(`\\n${renderAvatarBanner(opts)}\\n\\n`);\n}\n","// Wrapper around `gh repo create <org>/<name> --<vis> --source=<folder>\n// --remote=origin --push`. Stream stdio để user thấy gh prompt nếu có.\nimport { spawnSync } from \"node:child_process\";\nimport type { RepoVisibility } from \"./validate-repo-name-and-visibility.js\";\n\nexport class RepoAlreadyExistsError extends Error {\n constructor(fullName: string) {\n super(`Repo \"${fullName}\" đã tồn tại trên GitHub. Đổi tên hoặc xóa repo cũ.`);\n this.name = \"RepoAlreadyExistsError\";\n }\n}\n\nexport interface ExecuteGhRepoCreateInput {\n folder: string;\n org: string;\n name: string;\n visibility: RepoVisibility;\n}\n\nexport interface ExecuteGhRepoCreateOutput {\n sshUrl: string;\n httpsUrl: string;\n}\n\nexport function executeGhRepoCreate(input: ExecuteGhRepoCreateInput): ExecuteGhRepoCreateOutput {\n const fullName = `${input.org}/${input.name}`;\n const args = [\n \"repo\",\n \"create\",\n fullName,\n `--${input.visibility}`,\n \"--source\",\n input.folder,\n \"--remote\",\n \"origin\",\n \"--push\",\n ];\n const r = spawnSync(\"gh\", args, { stdio: \"inherit\" });\n if (r.status !== 0) {\n // gh thường in \"GraphQL: Name already exists\" cho duplicate. Không parse\n // stdout (vì inherit) — surface generic error, user sẽ thấy stderr của gh.\n if (r.status === 1) {\n throw new RepoAlreadyExistsError(fullName);\n }\n throw new Error(`gh repo create thất bại (exit ${r.status})`);\n }\n return {\n sshUrl: `git@github.com:${fullName}.git`,\n httpsUrl: `https://github.com/${fullName}.git`,\n };\n}\n","// Lấy GitHub login mặc định của user qua `gh api user --jq .login`.\n// Dùng làm default org khi user không truyền --repo-org.\nimport { spawnSync } from \"node:child_process\";\n\nexport function resolveGithubUsernameDefault(): string {\n const r = spawnSync(\"gh\", [\"api\", \"user\", \"--jq\", \".login\"], {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n if (r.status !== 0) {\n throw new Error(`Không lấy được GitHub username: ${r.stderr?.trim()}`);\n }\n return r.stdout.trim();\n}\n","// Validate repo name + visibility trước khi gọi `gh repo create`. Fail-fast với\n// thông báo Vietnamese rõ ràng. GitHub repo name spec: alphanumeric, dash,\n// underscore, dot. 1-100 chars.\nconst REPO_NAME_REGEX = /^[a-zA-Z0-9._-]{1,100}$/;\n\nexport type RepoVisibility = \"private\" | \"public\";\n\nexport class InvalidRepoNameError extends Error {\n constructor(name: string) {\n super(\n `Tên repo \"${name}\" không hợp lệ. Chỉ dùng chữ/số/dấu chấm/gạch/underscore, dài 1-100 ký tự.`,\n );\n this.name = \"InvalidRepoNameError\";\n }\n}\n\nexport function validateRepoName(name: string): void {\n if (!REPO_NAME_REGEX.test(name)) {\n throw new InvalidRepoNameError(name);\n }\n}\n\nexport function validateRepoVisibility(v: string): asserts v is RepoVisibility {\n if (v !== \"private\" && v !== \"public\") {\n throw new Error(`Visibility phải là \"private\" hoặc \"public\", nhận: \"${v}\"`);\n }\n}\n","// Orchestrator phase 4: validate input → gọi gh repo create → return URLs.\n// Caller chịu trách nhiệm đảm bảo gh đã auth (gọi ensureGitHubReady trước).\nimport { type ExecuteGhRepoCreateOutput, executeGhRepoCreate } from \"./execute-gh-repo-create.js\";\nimport { resolveGithubUsernameDefault } from \"./resolve-github-username-default.js\";\nimport { log } from \"./terminal-logger.js\";\nimport {\n type RepoVisibility,\n validateRepoName,\n validateRepoVisibility,\n} from \"./validate-repo-name-and-visibility.js\";\n\nexport interface CreateGithubRemoteInput {\n folder: string;\n name: string;\n visibility: RepoVisibility;\n org?: string; // mặc định = GitHub login của user\n}\n\nexport function createGithubRemoteFromFolder(\n input: CreateGithubRemoteInput,\n): ExecuteGhRepoCreateOutput {\n validateRepoName(input.name);\n validateRepoVisibility(input.visibility);\n\n const org = input.org ?? resolveGithubUsernameDefault();\n log.info(`Tạo GitHub repo ${org}/${input.name} (${input.visibility})...`);\n\n const urls = executeGhRepoCreate({\n folder: input.folder,\n org,\n name: input.name,\n visibility: input.visibility,\n });\n\n log.success(`Đã tạo: ${urls.sshUrl}`);\n return urls;\n}\n","// Kiểm tra gh CLI đã login chưa. `gh auth status` exit 0 = OK.\n// Không parse stdout vì format thay đổi giữa các bản gh.\nimport { spawnSync } from \"node:child_process\";\n\nexport type GhAuthState = \"not-installed\" | \"not-authenticated\" | \"authenticated\";\n\nexport function checkGhCliAuthStatus(): GhAuthState {\n // Probe binary trước. spawnSync với gh không tồn tại trả ENOENT.\n const r = spawnSync(\"gh\", [\"auth\", \"status\"], { stdio: \"ignore\" });\n if (r.error && (r.error as NodeJS.ErrnoException).code === \"ENOENT\") {\n return \"not-installed\";\n }\n return r.status === 0 ? \"authenticated\" : \"not-authenticated\";\n}\n","// Detect package manager để cài binary hệ thống (gh CLI). Order matters:\n// brew (macOS preferred) → winget (Windows) → apt/dnf/pacman (Linux distros).\n// Returns null nếu không tìm được PM nào — caller phải fail-fast.\nimport { spawnSync } from \"node:child_process\";\nimport { detectHostPlatform } from \"./detect-host-platform.js\";\n\nexport type PackageManager = \"brew\" | \"apt\" | \"dnf\" | \"pacman\" | \"winget\";\n\n// Probe binary có trong PATH không. Dùng `command -v` (POSIX) hoặc `where` (Win).\nfunction hasBinary(name: string): boolean {\n const platform = detectHostPlatform();\n const probe = platform === \"win32\" ? \"where\" : \"command\";\n const args = platform === \"win32\" ? [name] : [\"-v\", name];\n // spawnSync với shell=true để `command -v` (builtin) hoạt động.\n const r = spawnSync(probe, args, {\n shell: platform !== \"win32\",\n stdio: \"ignore\",\n });\n return r.status === 0;\n}\n\n// Trả về PM đầu tiên có sẵn theo thứ tự ưu tiên phù hợp với OS.\nexport function detectPackageManager(): PackageManager | null {\n const platform = detectHostPlatform();\n const candidates: PackageManager[] =\n platform === \"darwin\"\n ? [\"brew\"]\n : platform === \"win32\"\n ? [\"winget\"]\n : platform === \"linux\"\n ? [\"apt\", \"dnf\", \"pacman\"]\n : [];\n for (const pm of candidates) {\n if (hasBinary(pm)) return pm;\n }\n return null;\n}\n","// Wrapper mỏng quanh os.platform() để các module khác switch theo OS dễ hơn\n// và test dễ mock.\nimport { platform } from \"node:os\";\n\nexport type HostPlatform = \"darwin\" | \"linux\" | \"win32\" | \"unsupported\";\n\n// Trả về tên platform chuẩn hoá. Mọi giá trị khác 3 OS chính → \"unsupported\".\nexport function detectHostPlatform(): HostPlatform {\n const p = platform();\n if (p === \"darwin\" || p === \"linux\" || p === \"win32\") return p;\n return \"unsupported\";\n}\n","// Cài `gh` CLI qua package manager đã detect. Stream stdio để user thấy progress.\n// Throws nếu cài fail hoặc PM trả non-zero.\nimport { spawnSync } from \"node:child_process\";\nimport type { PackageManager } from \"./detect-package-manager.js\";\nimport { log } from \"./terminal-logger.js\";\n\n// Map PM → command + args để cài gh. apt/dnf cần sudo; brew/pacman/winget thì không.\nconst INSTALL_COMMANDS: Record<PackageManager, { cmd: string; args: string[] }> = {\n brew: { cmd: \"brew\", args: [\"install\", \"gh\"] },\n apt: { cmd: \"sudo\", args: [\"apt-get\", \"install\", \"-y\", \"gh\"] },\n dnf: { cmd: \"sudo\", args: [\"dnf\", \"install\", \"-y\", \"gh\"] },\n pacman: { cmd: \"sudo\", args: [\"pacman\", \"-S\", \"--noconfirm\", \"github-cli\"] },\n winget: { cmd: \"winget\", args: [\"install\", \"--id\", \"GitHub.cli\", \"-e\", \"--silent\"] },\n};\n\nexport function installGhCliViaPackageManager(pm: PackageManager): void {\n const spec = INSTALL_COMMANDS[pm];\n log.info(`Đang cài gh CLI qua ${pm}...`);\n const r = spawnSync(spec.cmd, spec.args, { stdio: \"inherit\" });\n if (r.status !== 0) {\n throw new Error(`Cài gh CLI thất bại qua ${pm} (exit ${r.status}). Cài tay rồi chạy lại.`);\n }\n log.success(\"Đã cài gh CLI\");\n}\n","// Spawn `gh auth login --hostname github.com --web` interactive.\n// User sẽ thấy device-code prompt và browser tự mở. Block đến khi xong.\nimport { spawnSync } from \"node:child_process\";\nimport { log } from \"./terminal-logger.js\";\n\nexport function triggerGhCliAuthLogin(): void {\n log.info(\"Khởi động đăng nhập GitHub qua gh CLI (browser sẽ mở)...\");\n const r = spawnSync(\n \"gh\",\n [\"auth\", \"login\", \"--hostname\", \"github.com\", \"--web\", \"--git-protocol\", \"ssh\"],\n { stdio: \"inherit\" },\n );\n if (r.status !== 0) {\n throw new Error(`gh auth login thất bại (exit ${r.status}). Thử 'gh auth login' tay.`);\n }\n log.success(\"Đã đăng nhập GitHub\");\n}\n","// Verify một remote URL có accessible không bằng `git ls-remote <url> HEAD`\n// với timeout 5 giây. Tách ra để init flow check sớm — fail-fast nếu URL sai.\nimport { spawnSync } from \"node:child_process\";\n\nconst TIMEOUT_MS = 5_000;\n\nexport class RemoteNotAccessibleError extends Error {\n constructor(url: string, reason: string) {\n super(`Không truy cập được remote ${url}: ${reason}`);\n this.name = \"RemoteNotAccessibleError\";\n }\n}\n\nexport function verifyGitRemoteAccessible(url: string): void {\n const r = spawnSync(\"git\", [\"ls-remote\", \"--exit-code\", url, \"HEAD\"], {\n stdio: \"ignore\",\n timeout: TIMEOUT_MS,\n });\n if (r.status === 0) return;\n // signal=SIGTERM nghĩa là Node.js kill do timeout.\n if (r.signal === \"SIGTERM\") throw new RemoteNotAccessibleError(url, \"timeout 5s\");\n throw new RemoteNotAccessibleError(url, `git ls-remote exit ${r.status}`);\n}\n","// Orchestrator phase 2: đảm bảo gh CLI có và đã auth. Tự cài + tự login nếu cần.\n// Chỉ throws khi không thể tự fix (PM thiếu, login thất bại).\nimport { checkGhCliAuthStatus } from \"./check-gh-cli-auth-status.js\";\nimport { detectPackageManager } from \"./detect-package-manager.js\";\nimport { installGhCliViaPackageManager } from \"./install-gh-cli-via-package-manager.js\";\nimport { log } from \"./terminal-logger.js\";\nimport { triggerGhCliAuthLogin } from \"./trigger-gh-cli-auth-login.js\";\nimport { verifyGitRemoteAccessible } from \"./verify-git-remote-accessible.js\";\n\n// Gọi trước mọi flow cần GitHub (init nhánh 1, nhánh 2-create-remote, nhánh 3).\n// remoteUrl optional — nếu truyền sẽ verify access cụ thể.\nexport async function ensureGitHubReady(remoteUrl?: string): Promise<void> {\n let state = checkGhCliAuthStatus();\n\n if (state === \"not-installed\") {\n log.warn(\"gh CLI chưa cài. Avatar sẽ tự cài.\");\n const pm = detectPackageManager();\n if (!pm) {\n throw new Error(\n \"Không phát hiện package manager (brew/apt/dnf/pacman/winget). Cài gh CLI tay rồi chạy lại: https://cli.github.com\",\n );\n }\n installGhCliViaPackageManager(pm);\n state = checkGhCliAuthStatus();\n }\n\n if (state === \"not-authenticated\") {\n log.warn(\"Chưa đăng nhập GitHub.\");\n triggerGhCliAuthLogin();\n state = checkGhCliAuthStatus();\n if (state !== \"authenticated\") {\n throw new Error(\"Sau gh auth login vẫn chưa authenticated. Thử lại.\");\n }\n }\n\n log.success(\"gh CLI sẵn sàng\");\n\n if (remoteUrl) {\n verifyGitRemoteAccessible(remoteUrl);\n log.success(`Remote accessible: ${remoteUrl}`);\n }\n}\n","// Kiểm tra folder đã là git repo chưa. Đơn giản: check tồn tại của \".git\"\n// (folder hoặc file gitlink — cả hai đều hợp lệ với submodule).\nimport { existsSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nexport function checkFolderHasGit(folderPath: string): boolean {\n const gitPath = join(folderPath, \".git\");\n if (!existsSync(gitPath)) return false;\n // .git có thể là directory (repo bình thường) hoặc file (submodule gitlink).\n // Cả 2 đều xem là \"đã có git\".\n const stat = statSync(gitPath);\n return stat.isDirectory() || stat.isFile();\n}\n","// Init git repo trong folder + tạo initial commit. Idempotent: skip nếu đã\n// có commit. Default branch \"main\".\nimport { simpleGit } from \"simple-git\";\n\nconst INITIAL_COMMIT_MESSAGE = \"chore: initial commit\";\n\nexport async function createInitialGitCommit(folderPath: string): Promise<void> {\n const g = simpleGit({ baseDir: folderPath });\n\n // Init nếu chưa có .git.\n const isRepo = await g.checkIsRepo().catch(() => false);\n if (!isRepo) {\n await g.init();\n }\n\n // Đảm bảo branch hiện tại là main (đổi master → main nếu cần).\n // Một số git config user có init.defaultBranch=master.\n try {\n await g.branch([\"-M\", \"main\"]);\n } catch {\n // Repo trống chưa có commit nào — branch -M sẽ fail. Bỏ qua, commit\n // đầu tiên dưới đây sẽ tạo branch main qua HEAD ref.\n }\n\n // Stage all + commit. Nếu không có file (folder rỗng) thì commit empty để\n // submodule add có HEAD reference dùng.\n await g.add(\".\");\n const status = await g.status();\n const hasCommits = (await g.raw([\"rev-list\", \"-n\", \"1\", \"--all\"]).catch(() => \"\")).trim();\n if (hasCommits) return; // Đã có commit, skip.\n\n if (status.files.length === 0) {\n await g.commit(INITIAL_COMMIT_MESSAGE, undefined, { \"--allow-empty\": null });\n } else {\n await g.commit(INITIAL_COMMIT_MESSAGE);\n }\n}\n","// Detect tech stack của folder qua signature file ở root. Trả về tất cả stack\n// match được (folder polyglot là chuyện thường — vd monorepo Node + Python).\n// Nếu không match gì → [\"generic\"].\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nexport type TechStack = \"node\" | \"python\" | \"go\" | \"rust\" | \"java\" | \"ruby\" | \"generic\";\n\n// Bảng signature: stack → các file đủ điều kiện claim stack đó.\nconst SIGNATURES: Record<Exclude<TechStack, \"generic\">, string[]> = {\n node: [\"package.json\"],\n python: [\"pyproject.toml\", \"requirements.txt\", \"setup.py\", \"Pipfile\"],\n go: [\"go.mod\"],\n rust: [\"Cargo.toml\"],\n java: [\"pom.xml\", \"build.gradle\", \"build.gradle.kts\"],\n ruby: [\"Gemfile\"],\n};\n\nexport function detectFolderTechStack(folderPath: string): TechStack[] {\n const matched: TechStack[] = [];\n for (const [stack, files] of Object.entries(SIGNATURES) as [\n Exclude<TechStack, \"generic\">,\n string[],\n ][]) {\n if (files.some((f) => existsSync(join(folderPath, f)))) {\n matched.push(stack);\n }\n }\n return matched.length > 0 ? matched : [\"generic\"];\n}\n","// Load template gitignore từ src/templates/gitignore/<stack>.txt và compose\n// nội dung tổng hợp cho 1+ stack. Generic luôn được prepend.\nimport { readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { TechStack } from \"./detect-folder-tech-stack.js\";\n\n// Resolve template dir tương đối với file này, không phải CWD. Khi bundle bằng\n// tsup, file này nằm trong dist/, templates nằm trong src/templates — tsup copy\n// templates qua --publicDir (đã cấu hình trong tsup.config.ts) hoặc resolve\n// qua relative path.\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\n// Thứ tự search: relative tới file build (dist/), fallback dev (src/lib/).\nconst CANDIDATE_DIRS = [\n join(__dirname, \"..\", \"templates\", \"gitignore\"),\n join(__dirname, \"..\", \"..\", \"src\", \"templates\", \"gitignore\"),\n];\n\nconst AVATAR_MARKER_START = \"# === avatar ===\";\nconst AVATAR_MARKER_END = \"# === /avatar ===\";\n\nfunction readTemplate(stack: TechStack): string {\n for (const dir of CANDIDATE_DIRS) {\n try {\n return readFileSync(join(dir, `${stack}.txt`), \"utf8\");\n } catch {\n // continue\n }\n }\n throw new Error(`Không tìm thấy template gitignore cho stack \"${stack}\"`);\n}\n\n// Compose: generic luôn ở đầu, sau đó các stack khác theo thứ tự detect.\n// Wrap trong marker để uninstall biết range gỡ chính xác.\nexport function composeGitignoreContent(stacks: TechStack[]): string {\n const all: TechStack[] = [\"generic\", ...stacks.filter((s) => s !== \"generic\")];\n const sections = all.map((s) => `# --- ${s} ---\\n${readTemplate(s).trim()}`);\n return [AVATAR_MARKER_START, ...sections, AVATAR_MARKER_END, \"\"].join(\"\\n\");\n}\n\nexport { AVATAR_MARKER_START, AVATAR_MARKER_END };\n","// Ghi .gitignore: tạo mới nếu chưa có, merge content nếu đã có. Merge logic\n// dùng marker `# === avatar === ... # === /avatar ===`: replace block đó nếu\n// tồn tại, append nếu chưa.\nimport { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { AVATAR_MARKER_END, AVATAR_MARKER_START } from \"./gitignore-template-loader.js\";\n\nexport function writeOrMergeGitignore(folderPath: string, avatarBlock: string): void {\n const path = join(folderPath, \".gitignore\");\n\n if (!existsSync(path)) {\n writeFileSync(path, avatarBlock, \"utf8\");\n return;\n }\n\n const existing = readFileSync(path, \"utf8\");\n const startIdx = existing.indexOf(AVATAR_MARKER_START);\n const endIdx = existing.indexOf(AVATAR_MARKER_END);\n\n // Đã có marker → replace block giữa marker (giữ content trước/sau).\n if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {\n const before = existing.slice(0, startIdx);\n const after = existing.slice(endIdx + AVATAR_MARKER_END.length);\n writeFileSync(path, `${before.trimEnd()}\\n\\n${avatarBlock}${after.trimStart()}`, \"utf8\");\n return;\n }\n\n // Chưa có marker → append phía cuối, thêm newline phân cách.\n writeFileSync(path, `${existing.trimEnd()}\\n\\n${avatarBlock}`, \"utf8\");\n}\n","// Orchestrator phase 3: bootstrap git cho folder local. Idempotent — chạy trên\n// folder đã có git sẽ no-op (chỉ ensure .gitignore có Avatar block).\nimport { checkFolderHasGit } from \"./check-folder-has-git.js\";\nimport { createInitialGitCommit } from \"./create-initial-git-commit.js\";\nimport { detectFolderTechStack } from \"./detect-folder-tech-stack.js\";\nimport { composeGitignoreContent } from \"./gitignore-template-loader.js\";\nimport { log } from \"./terminal-logger.js\";\nimport { writeOrMergeGitignore } from \"./write-or-merge-gitignore.js\";\n\nexport async function bootstrapGitInFolder(folderPath: string): Promise<void> {\n const hadGit = checkFolderHasGit(folderPath);\n\n // Auto detect tech stack + write .gitignore với Avatar block.\n const stacks = detectFolderTechStack(folderPath);\n log.info(`Tech stack detected: ${stacks.join(\", \")}`);\n writeOrMergeGitignore(folderPath, composeGitignoreContent(stacks));\n log.success(\".gitignore đã ghi (Avatar block)\");\n\n if (!hadGit) {\n log.info(`Bootstrap git cho ${folderPath}...`);\n await createInitialGitCommit(folderPath);\n log.success(\"Đã git init + initial commit\");\n } else {\n log.dim(\"Folder đã có .git — skip init.\");\n }\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 { printAvatarBanner } from \"../lib/avatar-ascii-banner.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 // Banner trước khi vào device-code flow để user nhận diện thương hiệu.\n printAvatarBanner({ tagline: \"Đăng nhập Google SSO · workspace @nal.vn\" });\n\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","// `avatar uninstall` — Command 13 (v1.1).\n// Gỡ Avatar khỏi project + auto-backup vào ~/.avatar/uninstall-backups/.\n// Đối ứng với `avatar init`. Code khách (src/) giữ nguyên.\n\nimport { relative } from \"node:path\";\nimport { confirm } from \"@inquirer/prompts\";\nimport boxen from \"boxen\";\nimport type { Command } from \"commander\";\nimport { appendAuditEntry } from \"../lib/audit-log-appender.js\";\nimport { createUninstallBackupSnapshot } from \"../lib/create-uninstall-backup-snapshot.js\";\nimport { detectAvatarProjectArtifacts } from \"../lib/detect-avatar-project-artifacts.js\";\nimport { executeUninstallDeletion } from \"../lib/execute-uninstall-deletion.js\";\nimport { chalk, log } from \"../lib/terminal-logger.js\";\n\nconst CLI_VERSION = \"1.1.1\";\n\ninterface UninstallOptions {\n yes?: boolean;\n noBackup?: boolean;\n keepSubmodule?: boolean;\n keepHooks?: boolean;\n dryRun?: boolean;\n}\n\nexport function registerUninstallCommand(program: Command): void {\n program\n .command(\"uninstall\")\n .description(\"Gỡ Avatar khỏi project — backup tự động (M11)\")\n .option(\"--yes\", \"Skip confirm prompt\")\n .option(\"--no-backup\", \"Không tạo backup trước khi xóa (nguy hiểm)\")\n .option(\"--keep-submodule\", \"Giữ submodule .claude/pack/\")\n .option(\"--keep-hooks\", \"Giữ git hooks post-merge, pre-push\")\n .option(\"--dry-run\", \"Hiển thị danh sách sẽ xóa, không thực thi\")\n .action(async (opts: UninstallOptions) => {\n try {\n await runUninstall(opts);\n } catch (err) {\n log.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n });\n}\n\nasync function runUninstall(opts: UninstallOptions): Promise<void> {\n const projectRoot = process.cwd();\n const artifacts = detectAvatarProjectArtifacts(projectRoot);\n\n if (!artifacts.hasAnyArtifact) {\n log.info(\"Project chưa cài Avatar — không có gì để gỡ.\");\n return;\n }\n\n // Show summary.\n printUninstallSummary(projectRoot, artifacts, opts);\n\n if (opts.dryRun) {\n log.dim(\"--dry-run: kết thúc, không xóa.\");\n return;\n }\n\n // Confirm.\n if (!opts.yes) {\n const ok = await confirm({\n message: \"Tiếp tục gỡ Avatar?\",\n default: false,\n });\n if (!ok) {\n log.info(\"Đã hủy.\");\n return;\n }\n }\n\n // Backup (trừ khi --no-backup).\n let backupPath: string | null = null;\n if (!opts.noBackup) {\n backupPath = await createUninstallBackupSnapshot(projectRoot, artifacts, CLI_VERSION);\n log.success(`Backup tạo tại: ${backupPath}`);\n }\n\n // Delete artifacts.\n await executeUninstallDeletion(artifacts, {\n keepSubmodule: opts.keepSubmodule,\n keepHooks: opts.keepHooks,\n });\n\n await appendAuditEntry(\"uninstall\", `project=${projectRoot},backup=${backupPath ?? \"skipped\"}`);\n\n printUninstallSuccessBox(backupPath);\n}\n\nfunction printUninstallSummary(\n projectRoot: string,\n artifacts: ReturnType<typeof detectAvatarProjectArtifacts>,\n opts: UninstallOptions,\n): void {\n log.info(`Project: ${projectRoot}`);\n log.plain(\"\");\n log.plain(\"Các artifact sẽ gỡ:\");\n if (artifacts.claudeDir)\n log.plain(` ${chalk.red(\"✗\")} ${relative(projectRoot, artifacts.claudeDir) || \".claude/\"}`);\n if (artifacts.claudeMd) log.plain(` ${chalk.red(\"✗\")} CLAUDE.md`);\n if (artifacts.postMergeHook && !opts.keepHooks) {\n log.plain(` ${chalk.red(\"✗\")} .git/hooks/post-merge`);\n }\n if (artifacts.prePushHook && !opts.keepHooks) {\n log.plain(` ${chalk.red(\"✗\")} .git/modules/src/hooks/pre-push`);\n }\n if (artifacts.gitignorePath) log.plain(` ${chalk.yellow(\"✎\")} .gitignore (gỡ Avatar block)`);\n if (artifacts.gitmodulesPath && !opts.keepSubmodule) {\n log.plain(` ${chalk.yellow(\"✎\")} .gitmodules (gỡ entry .claude/pack)`);\n }\n log.plain(\"\");\n log.plain(\"Không đụng:\");\n log.plain(` ${chalk.green(\"✓\")} src/ (code khách)`);\n log.plain(` ${chalk.green(\"✓\")} Git history`);\n log.plain(` ${chalk.green(\"✓\")} ~/.avatar/config.json (token SSO)`);\n log.plain(` ${chalk.green(\"✓\")} Secrets trong keychain`);\n log.plain(\"\");\n}\n\nfunction printUninstallSuccessBox(backupPath: string | null): void {\n const lines: string[] = [`${chalk.green(\"✓\")} Avatar đã được gỡ khỏi project`];\n if (backupPath) {\n lines.push(\"\");\n lines.push(` ${chalk.dim(\"Backup:\")} ${backupPath}`);\n lines.push(` ${chalk.dim(\"Restore:\")} ${chalk.cyan(`cp -r \"${backupPath}\"/* .`)}`);\n }\n process.stdout.write(`${boxen(lines.join(\"\\n\"), { padding: 1, borderStyle: \"round\" })}\\n`);\n}\n","// Tạo snapshot backup trước khi uninstall. Folder: ~/.avatar/uninstall-backups/\n// <project-name>-<ts>/ với cấu trúc trong spec doc.\nimport { cp, mkdir, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { basename, join } from \"node:path\";\nimport type { AvatarProjectArtifacts } from \"./detect-avatar-project-artifacts.js\";\n\nexport interface BackupManifest {\n projectName: string;\n projectPath: string;\n timestamp: string;\n avatarVersion: string;\n artifacts: {\n claudeDir: boolean;\n claudeMd: boolean;\n postMergeHook: boolean;\n prePushHook: boolean;\n };\n}\n\nconst UNINSTALL_BACKUPS_DIR = join(homedir(), \".avatar\", \"uninstall-backups\");\n\nexport async function createUninstallBackupSnapshot(\n projectRoot: string,\n artifacts: AvatarProjectArtifacts,\n avatarVersion: string,\n): Promise<string> {\n const projectName = basename(projectRoot);\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n const backupDir = join(UNINSTALL_BACKUPS_DIR, `${projectName}-${timestamp}`);\n\n await mkdir(backupDir, { recursive: true, mode: 0o700 });\n\n // Copy .claude/ và CLAUDE.md nếu tồn tại.\n if (artifacts.claudeDir) {\n await cp(artifacts.claudeDir, join(backupDir, \".claude\"), { recursive: true });\n }\n if (artifacts.claudeMd) {\n await cp(artifacts.claudeMd, join(backupDir, \"CLAUDE.md\"));\n }\n\n // Copy hooks sang backup/hooks/.\n if (artifacts.postMergeHook || artifacts.prePushHook) {\n const hooksBackupDir = join(backupDir, \"hooks\");\n await mkdir(hooksBackupDir, { recursive: true });\n if (artifacts.postMergeHook) {\n await cp(artifacts.postMergeHook, join(hooksBackupDir, \"post-merge\"));\n }\n if (artifacts.prePushHook) {\n await cp(artifacts.prePushHook, join(hooksBackupDir, \"pre-push\"));\n }\n }\n\n // Write manifest.\n const manifest: BackupManifest = {\n projectName,\n projectPath: projectRoot,\n timestamp,\n avatarVersion,\n artifacts: {\n claudeDir: !!artifacts.claudeDir,\n claudeMd: !!artifacts.claudeMd,\n postMergeHook: !!artifacts.postMergeHook,\n prePushHook: !!artifacts.prePushHook,\n },\n };\n await writeFile(join(backupDir, \"manifest.json\"), JSON.stringify(manifest, null, 2), \"utf8\");\n\n return backupDir;\n}\n","// Scan project root để liệt kê các file/folder Avatar đã tạo. Output là blueprint\n// cho uninstall: cái gì sẽ xóa + cái gì sẽ edit (gitignore, gitmodules).\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nexport interface AvatarProjectArtifacts {\n hasAnyArtifact: boolean;\n claudeDir: string | null; // .claude/\n claudeMd: string | null; // CLAUDE.md\n postMergeHook: string | null; // .git/hooks/post-merge\n prePushHook: string | null; // .git/modules/src/hooks/pre-push\n gitignorePath: string | null; // .gitignore (nếu có Avatar block)\n gitmodulesPath: string | null; // .gitmodules (nếu có submodule .claude/pack)\n notesDir: string | null; // notes/ (workspace mode)\n scriptsDir: string | null; // scripts/ (workspace mode)\n}\n\nfunction existsOrNull(path: string): string | null {\n return existsSync(path) ? path : null;\n}\n\nexport function detectAvatarProjectArtifacts(projectRoot: string): AvatarProjectArtifacts {\n const claudeDir = existsOrNull(join(projectRoot, \".claude\"));\n const claudeMd = existsOrNull(join(projectRoot, \"CLAUDE.md\"));\n const postMergeHook = existsOrNull(join(projectRoot, \".git\", \"hooks\", \"post-merge\"));\n const prePushHook = existsOrNull(\n join(projectRoot, \".git\", \"modules\", \"src\", \"hooks\", \"pre-push\"),\n );\n const gitignorePath = existsOrNull(join(projectRoot, \".gitignore\"));\n const gitmodulesPath = existsOrNull(join(projectRoot, \".gitmodules\"));\n const notesDir = existsOrNull(join(projectRoot, \"notes\"));\n const scriptsDir = existsOrNull(join(projectRoot, \"scripts\"));\n\n const hasAnyArtifact = !!(claudeDir || claudeMd || postMergeHook || prePushHook);\n\n return {\n hasAnyArtifact,\n claudeDir,\n claudeMd,\n postMergeHook,\n prePushHook,\n gitignorePath,\n gitmodulesPath,\n notesDir,\n scriptsDir,\n };\n}\n","// Atomic delete các artifact Avatar khỏi project. Gỡ marker block trong\n// .gitignore, remove submodule entry trong .gitmodules. Không đụng src/ + git\n// history + user config.\nimport { readFile, rm, writeFile } from \"node:fs/promises\";\nimport type { AvatarProjectArtifacts } from \"./detect-avatar-project-artifacts.js\";\nimport { AVATAR_MARKER_END, AVATAR_MARKER_START } from \"./gitignore-template-loader.js\";\n\nexport interface UninstallFlags {\n keepSubmodule?: boolean;\n keepHooks?: boolean;\n}\n\nexport async function executeUninstallDeletion(\n artifacts: AvatarProjectArtifacts,\n flags: UninstallFlags,\n): Promise<void> {\n // Delete .claude/ (trừ khi --keep-submodule muốn giữ pack/ — thực tế cả\n // .claude/ chứa nhiều thứ khác, nên --keep-submodule chỉ giữ pack/).\n if (artifacts.claudeDir) {\n if (flags.keepSubmodule) {\n // Chỉ xóa các file/folder không phải pack/ trong .claude/.\n const { readdir } = await import(\"node:fs/promises\");\n const { join } = await import(\"node:path\");\n const entries = await readdir(artifacts.claudeDir);\n for (const entry of entries) {\n if (entry === \"pack\") continue;\n await rm(join(artifacts.claudeDir, entry), { recursive: true, force: true });\n }\n } else {\n await rm(artifacts.claudeDir, { recursive: true, force: true });\n }\n }\n\n if (artifacts.claudeMd) {\n await rm(artifacts.claudeMd, { force: true });\n }\n\n if (!flags.keepHooks) {\n if (artifacts.postMergeHook) await rm(artifacts.postMergeHook, { force: true });\n if (artifacts.prePushHook) await rm(artifacts.prePushHook, { force: true });\n }\n\n // Strip Avatar block khỏi .gitignore (giữ rest).\n if (artifacts.gitignorePath) {\n await stripAvatarBlockFromGitignore(artifacts.gitignorePath);\n }\n\n // Remove submodule entry .claude/pack khỏi .gitmodules (nếu xóa cả pack).\n if (artifacts.gitmodulesPath && !flags.keepSubmodule) {\n await removeSubmoduleEntry(artifacts.gitmodulesPath, \".claude/pack\");\n }\n\n // Workspace mode có notes/, scripts/. Chỉ xóa nếu rỗng (user có thể đã add file).\n for (const dir of [artifacts.notesDir, artifacts.scriptsDir]) {\n if (!dir) continue;\n const { readdir } = await import(\"node:fs/promises\");\n const entries = await readdir(dir);\n if (entries.length === 0) {\n await rm(dir, { recursive: true, force: true });\n }\n }\n}\n\nasync function stripAvatarBlockFromGitignore(path: string): Promise<void> {\n const content = await readFile(path, \"utf8\");\n const startIdx = content.indexOf(AVATAR_MARKER_START);\n const endIdx = content.indexOf(AVATAR_MARKER_END);\n if (startIdx === -1 || endIdx === -1) return;\n\n const before = content.slice(0, startIdx);\n const after = content.slice(endIdx + AVATAR_MARKER_END.length);\n const cleaned = `${before.trimEnd()}\\n${after.trimStart()}`.trim();\n if (cleaned.length === 0) {\n await rm(path, { force: true });\n } else {\n await writeFile(path, `${cleaned}\\n`, \"utf8\");\n }\n}\n\nasync function removeSubmoduleEntry(gitmodulesPath: string, submodulePath: string): Promise<void> {\n const content = await readFile(gitmodulesPath, \"utf8\");\n const lines = content.split(\"\\n\");\n const result: string[] = [];\n let skip = false;\n for (const line of lines) {\n if (line.trim().startsWith(\"[submodule\") && line.includes(submodulePath)) {\n skip = true;\n continue;\n }\n if (skip && line.trim().startsWith(\"[submodule\")) {\n skip = false;\n }\n if (!skip) result.push(line);\n }\n const cleaned = result.join(\"\\n\").trim();\n if (cleaned.length === 0) {\n await rm(gitmodulesPath, { force: true });\n } else {\n await writeFile(gitmodulesPath, `${cleaned}\\n`, \"utf8\");\n }\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","chalk","spawnSync","input","spawnSync","input","spawnSync","spawnSync","platform","spawnSync","spawnSync","spawnSync","spawnSync","spawnSync","spawnSync","existsSync","join","simpleGit","existsSync","join","dirname","join","fileURLToPath","existsSync","readFileSync","join","join","existsSync","readFileSync","join","join","join","join","program","join","relative","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","relative","confirm","boxen","homedir","basename","join","existsSync","join","writeFile","readdir","join","writeFile","program","confirm","relative","boxen","CLI_VERSION"]}
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/avatar-ascii-banner.ts","../src/lib/execute-gh-repo-create.ts","../src/lib/resolve-github-username-default.ts","../src/lib/validate-repo-name-and-visibility.ts","../src/lib/create-github-remote-from-folder.ts","../src/lib/check-gh-cli-auth-status.ts","../src/lib/detect-package-manager.ts","../src/lib/detect-host-platform.ts","../src/lib/install-gh-cli-via-package-manager.ts","../src/lib/setup-git-credential-via-gh.ts","../src/lib/trigger-gh-cli-auth-login.ts","../src/lib/verify-git-remote-accessible.ts","../src/lib/git-auth-and-install-orchestrator.ts","../src/lib/check-folder-has-git.ts","../src/lib/create-initial-git-commit.ts","../src/lib/detect-folder-tech-stack.ts","../src/lib/gitignore-template-loader.ts","../src/lib/write-or-merge-gitignore.ts","../src/lib/git-bootstrap-orchestrator.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","../src/commands/uninstall.ts","../src/lib/create-uninstall-backup-snapshot.ts","../src/lib/detect-avatar-project-artifacts.ts","../src/lib/execute-uninstall-deletion.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\";\nimport { registerUninstallCommand } from \"./commands/uninstall.js\";\nimport { printAvatarBanner, renderAvatarBanner } from \"./lib/avatar-ascii-banner.js\";\n\nconst CLI_VERSION = \"1.1.2\";\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 // Override mặc định của commander để in banner kèm version. Commander cho phép\n // custom output qua configureOutput nhưng cách đơn giản nhất là intercept ở argv.\n .addHelpText(\n \"beforeAll\",\n () =>\n `\\n${renderAvatarBanner({ tagline: `v${CLI_VERSION} · AI harness CLI for NAL Vietnam` })}\\n\\n`,\n );\n\n// In banner khi user gọi `avatar -v` / `avatar --version`. Banner đã chứa\n// version trong tagline nên ta short-circuit luôn, không để commander in thêm\n// dòng \"1.0.1\" dư thừa.\nconst isVersionCall = process.argv.includes(\"-v\") || process.argv.includes(\"--version\");\nif (isVersionCall) {\n printAvatarBanner({ tagline: `v${CLI_VERSION} · AI harness CLI for NAL Vietnam` });\n process.exit(0);\n}\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);\nregisterUninstallCommand(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 (v1.1 redesign).\n//\n// Bỏ khái niệm --mode internal/client/library. Thay bằng wizard 3-câu hỏi tự\n// nhận diện tình trạng dự án:\n// 1. Đã có repo git remote (URL có sẵn) → flow=existing-remote\n// 2. Đã có folder code local → flow=existing-folder\n// 3. Dự án mới hoàn toàn → flow=new-project\n//\n// Mọi flow đều scaffold workspace tách biệt (không còn mode internal). Avatar\n// luôn check + tự cài gh CLI nếu thiếu, auto-bootstrap git cho folder chưa\n// init, auto-detect .gitignore theo tech stack.\n\nimport { basename, 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 { printAvatarBanner } from \"../lib/avatar-ascii-banner.js\";\nimport { checkFolderHasGit } from \"../lib/check-folder-has-git.js\";\nimport { createGithubRemoteFromFolder } from \"../lib/create-github-remote-from-folder.js\";\nimport { ensureDir } from \"../lib/filesystem-helpers.js\";\nimport { ensureGitHubReady } from \"../lib/git-auth-and-install-orchestrator.js\";\nimport { bootstrapGitInFolder } from \"../lib/git-bootstrap-orchestrator.js\";\nimport { git } 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 { 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 { RepoVisibility } from \"../lib/validate-repo-name-and-visibility.js\";\nimport {\n findAlternativeWorkspaceName,\n isEmptyOrMissing,\n} from \"./init-conflict-detection-helpers.js\";\nimport { buildScaffoldVariables, inferWorkspaceName } from \"./init-scaffold-variable-builders.js\";\n\n// 3 flow values + deprecated legacy \"mode\" alias.\ntype ProjectStatus = \"existing-remote\" | \"existing-folder\" | \"new-project\";\n\ninterface InitOptions {\n // New v1.1 flags\n projectStatus?: ProjectStatus;\n folderPath?: string;\n createRemote?: boolean;\n repoVisibility?: string;\n repoOrg?: string;\n // Carried over\n skipScan?: boolean;\n packVersion?: string;\n clientRepo?: string; // reused: URL khi projectStatus=existing-remote\n workspaceName?: string;\n workspaceParent?: string;\n force?: boolean;\n teamOwner?: string;\n description?: string;\n yes?: boolean;\n // Legacy (deprecated)\n mode?: string;\n}\n\nexport function registerInitCommand(program: Command): void {\n program\n .command(\"init\")\n .description(\"Khởi tạo Avatar — 3 flow tự nhận diện (repo / folder / new)\")\n .option(\"--project-status <val>\", \"existing-remote | existing-folder | new-project\")\n .option(\"--folder-path <path>\", \"Đường dẫn folder hiện có (flow existing-folder)\")\n .option(\"--create-remote\", \"Force tạo remote qua gh (flow existing-folder hoặc new-project)\")\n .option(\"--repo-visibility <val>\", \"private (mặc định) | public\")\n .option(\"--repo-org <name>\", \"GitHub org/owner cho repo mới\")\n .option(\"--client-repo <url>\", \"URL git remote (flow existing-remote)\")\n .option(\"--workspace-name <name>\", \"Tên workspace\")\n .option(\"--workspace-parent <path>\", \"Thư mục cha tạo workspace (mặc định ..)\")\n .option(\"--pack-version <tag>\", \"Pin team-ai-pack vào tag cụ thể\")\n .option(\"--team-owner <email>\", \"Email team owner (bỏ qua prompt)\")\n .option(\"--description <text>\", \"Mô tả 1 dòng của dự án\")\n .option(\"--skip-scan\", \"Bỏ qua project-scanner sau scaffold\")\n .option(\"--force\", \"Bỏ qua prompt khi workspace path đã tồn tại\")\n .option(\"--yes\", \"Auto-confirm tất cả prompt\")\n .option(\"--mode <mode>\", \"[DEPRECATED] Dùng --project-status thay thế\")\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 if (!opts.yes) printAvatarBanner({ tagline: \"Khởi tạo Avatar trong dự án của bạn\" });\n\n if (opts.mode) {\n log.warn(\"Flag --mode đã deprecated từ v1.1. Dùng --project-status thay thế.\");\n }\n\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 const status = opts.projectStatus ?? (await promptProjectStatus());\n\n switch (status) {\n case \"existing-remote\":\n await runInitFromExistingRemote(opts, userConfig.email);\n break;\n case \"existing-folder\":\n await runInitFromExistingFolder(opts, userConfig.email);\n break;\n case \"new-project\":\n await runInitFromScratch(opts, userConfig.email);\n break;\n }\n}\n\nasync function promptProjectStatus(): Promise<ProjectStatus> {\n return (await select({\n message: \"Tình trạng dự án của bạn?\",\n choices: [\n { name: \"1. Đã có repo git remote (URL có sẵn)\", value: \"existing-remote\" as const },\n { name: \"2. Đã có folder code local\", value: \"existing-folder\" as const },\n { name: \"3. Dự án mới hoàn toàn\", value: \"new-project\" as const },\n ],\n })) as ProjectStatus;\n}\n\n// ─── FLOW 1: EXISTING REMOTE ────────────────────────────────────────────────\nasync function runInitFromExistingRemote(opts: InitOptions, ownerEmail: string): Promise<void> {\n const remoteUrl =\n opts.clientRepo ??\n (await input({\n message: \"URL git của repo:\",\n validate: (v) => (v.length > 0 ? true : \"URL bắt buộc\"),\n }));\n\n await ensureGitHubReady(remoteUrl);\n\n const teamOwner = opts.teamOwner ?? (await promptTeamOwner(ownerEmail));\n const inferredName = inferWorkspaceName(remoteUrl);\n const workspaceName =\n opts.workspaceName ?? (await input({ message: \"Tên workspace:\", default: inferredName }));\n const workspaceParent = resolve(opts.workspaceParent ?? \"..\");\n const workspacePath = await resolveWorkspacePath(workspaceParent, workspaceName, opts.force);\n\n await scaffoldWorkspaceWithSrcSubmodule({\n workspacePath,\n workspaceName,\n srcRemoteUrl: remoteUrl,\n teamOwner,\n description: opts.description ?? `Avatar workspace cho ${remoteUrl}`,\n packVersion: opts.packVersion,\n autoYes: opts.yes,\n flow: \"existing-remote\",\n });\n}\n\n// ─── FLOW 2: EXISTING FOLDER ────────────────────────────────────────────────\nasync function runInitFromExistingFolder(opts: InitOptions, ownerEmail: string): Promise<void> {\n const folderPath = resolve(\n opts.folderPath ??\n (await input({\n message: \"Đường dẫn folder hiện có:\",\n validate: (v) => (v.length > 0 ? true : \"Path bắt buộc\"),\n })),\n );\n\n // Bootstrap git nếu chưa có (đồng thời ghi .gitignore Avatar block).\n await bootstrapGitInFolder(folderPath);\n\n // Check remote origin. Có → dùng luôn. Chưa có → hỏi tạo mới (default Có).\n const remoteUrl = await getOrCreateOriginRemote(folderPath, opts);\n\n const teamOwner = opts.teamOwner ?? (await promptTeamOwner(ownerEmail));\n const inferredName = opts.workspaceName ?? `${basename(folderPath)}-avatar-workspace`;\n const workspaceName =\n opts.workspaceName ?? (await input({ message: \"Tên workspace:\", default: inferredName }));\n const workspaceParent = resolve(opts.workspaceParent ?? \"..\");\n const workspacePath = await resolveWorkspacePath(workspaceParent, workspaceName, opts.force);\n\n await scaffoldWorkspaceWithSrcSubmodule({\n workspacePath,\n workspaceName,\n srcRemoteUrl: remoteUrl ?? folderPath, // fallback local path nếu user từ chối tạo remote\n teamOwner,\n description: opts.description ?? `Avatar workspace cho folder ${folderPath}`,\n packVersion: opts.packVersion,\n autoYes: opts.yes,\n flow: \"existing-folder\",\n });\n}\n\n// ─── FLOW 3: NEW PROJECT ────────────────────────────────────────────────────\nasync function runInitFromScratch(opts: InitOptions, ownerEmail: string): Promise<void> {\n await ensureGitHubReady();\n\n const projectName =\n opts.workspaceName ??\n (await input({\n message: \"Tên dự án:\",\n validate: (v) => (v.length > 0 ? true : \"Tên bắt buộc\"),\n }));\n const visibility = (opts.repoVisibility ??\n (await select({\n message: \"Visibility?\",\n choices: [\n { name: \"private (mặc định)\", value: \"private\" as const },\n { name: \"public\", value: \"public\" as const },\n ],\n }))) as RepoVisibility;\n\n const teamOwner = opts.teamOwner ?? (await promptTeamOwner(ownerEmail));\n const workspaceParent = resolve(opts.workspaceParent ?? \".\");\n const workspacePath = await resolveWorkspacePath(workspaceParent, projectName, opts.force);\n const srcPath = join(workspacePath, \"src\");\n\n // Tạo workspace dir + src/ rỗng + bootstrap git trong src/.\n await ensureDir(workspacePath);\n await ensureDir(srcPath);\n await bootstrapGitInFolder(srcPath);\n\n // Tạo remote GitHub + push initial commit.\n const urls = createGithubRemoteFromFolder({\n folder: srcPath,\n name: projectName,\n visibility,\n org: opts.repoOrg,\n });\n\n // Workspace setup: init git, submodule add cho src/.\n await git(workspacePath).init();\n const sp = spinner(\"Add submodule src/ + team-ai-pack...\");\n try {\n await git(workspacePath).subModule([\"add\", urls.sshUrl, \"src\"]);\n const result = await addTeamPackSubmodule(workspacePath, opts.packVersion);\n sp.succeed(`Pin team-ai-pack vào ${result.pinnedTag ?? \"HEAD\"}`);\n await finalizeWorkspaceScaffold({\n workspacePath,\n workspaceName: projectName,\n teamOwner,\n description: opts.description ?? `Dự án mới: ${projectName}`,\n packVersion: result.pinnedTag ?? \"HEAD\",\n autoYes: opts.yes,\n flow: \"new-project\",\n });\n } catch (err) {\n sp.fail(\"Init workspace thất bại\");\n throw err;\n }\n}\n\n// ─── HELPERS ────────────────────────────────────────────────────────────────\n\n// Check origin remote của folder; nếu chưa có thì hỏi tạo (default Có) → gh repo create.\n// Trả về URL remote nếu có, undefined nếu user từ chối (caller fallback local path).\nasync function getOrCreateOriginRemote(\n folderPath: string,\n opts: InitOptions,\n): Promise<string | undefined> {\n const remotes = await git(folderPath).getRemotes(true);\n const origin = remotes.find((r) => r.name === \"origin\");\n if (origin?.refs.push) {\n log.success(`Folder đã có remote origin: ${origin.refs.push}`);\n return origin.refs.push;\n }\n\n const shouldCreate =\n opts.createRemote ??\n (await confirm({\n message: \"Folder chưa có remote. Tạo GitHub repo ngay để share team?\",\n default: true,\n }));\n if (!shouldCreate) {\n log.warn(\"Tiếp tục với local path. Workspace chỉ chạy được trên máy bạn.\");\n return undefined;\n }\n\n await ensureGitHubReady();\n const visibility = (opts.repoVisibility ??\n (await select({\n message: \"Visibility?\",\n choices: [\n { name: \"private (mặc định)\", value: \"private\" as const },\n { name: \"public\", value: \"public\" as const },\n ],\n }))) as RepoVisibility;\n const repoName = await input({\n message: \"Tên repo:\",\n default: basename(folderPath),\n });\n const urls = createGithubRemoteFromFolder({\n folder: folderPath,\n name: repoName,\n visibility,\n org: opts.repoOrg,\n });\n return urls.sshUrl;\n}\n\n// Scaffold workspace dùng cho flow 1 + flow 2: workspace = parent của src/.\n// src/ là submodule trỏ tới remote URL hoặc local path.\nasync function scaffoldWorkspaceWithSrcSubmodule(args: {\n workspacePath: string;\n workspaceName: string;\n srcRemoteUrl: string;\n teamOwner: string;\n description: string;\n packVersion?: string;\n autoYes?: boolean;\n flow: ProjectStatus;\n}): Promise<void> {\n await ensureDir(args.workspacePath);\n await git(args.workspacePath).init();\n\n const sp = spinner(\"Add submodule src/ + team-ai-pack...\");\n try {\n await git(args.workspacePath).subModule([\"add\", args.srcRemoteUrl, \"src\"]);\n const result = await addTeamPackSubmodule(args.workspacePath, args.packVersion);\n sp.succeed(`Pin team-ai-pack vào ${result.pinnedTag ?? \"HEAD\"}`);\n\n await finalizeWorkspaceScaffold({\n workspacePath: args.workspacePath,\n workspaceName: args.workspaceName,\n teamOwner: args.teamOwner,\n description: args.description,\n packVersion: result.pinnedTag ?? \"HEAD\",\n autoYes: args.autoYes,\n flow: args.flow,\n });\n } catch (err) {\n sp.fail(\"Init workspace thất bại\");\n throw err;\n }\n}\n\n// Common scaffold step sau khi src/ + team-ai-pack đã add xong.\nasync function finalizeWorkspaceScaffold(args: {\n workspacePath: string;\n workspaceName: string;\n teamOwner: string;\n description: string;\n packVersion: string;\n autoYes?: boolean;\n flow: ProjectStatus;\n}): Promise<void> {\n // Mode \"client\" được dùng trong scaffold-variable builder cũ — giữ giá trị\n // này để compatible với template hiện tại. Sẽ rename trong release sau.\n const vars = buildScaffoldVariables({\n projectName: args.workspaceName,\n projectDescription: args.description,\n teamOwner: args.teamOwner,\n packVersion: args.packVersion,\n mode: \"client\",\n });\n\n await createClaudeDirTree(args.workspacePath);\n await writeProjectKnowledgeFiles(args.workspacePath, vars);\n await writeRootClaudeMd(args.workspacePath, vars);\n await writeProjectSettings(args.workspacePath, vars);\n await appendGitignoreEntries(args.workspacePath);\n await ensureDir(join(args.workspacePath, \"notes\"));\n await ensureDir(join(args.workspacePath, \"scripts\"));\n\n await installGitHook(join(args.workspacePath, \".git\"), \"post-merge\");\n await installGitHook(join(args.workspacePath, \".git\", \"modules\", \"src\"), \"pre-push\");\n log.success(\"Cài post-merge (workspace) + pre-push (src/)\");\n\n await appendAuditEntry(\"init\", `flow=${args.flow},workspace=${args.workspaceName}`);\n await maybeCommitWorkspace(args.workspacePath, args.autoYes);\n printInitSuccessBox(args.workspacePath, args.flow);\n}\n\n// ─── shared utilities (giữ từ v1.0.1, chỉ đổi signature mode → flow) ───────\n\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({ message: `Dùng \"${alternative}\" thay thế?`, default: true });\n if (!useAlt) throw new Error(\"Hủy init. Chạy lại với --workspace-name khác.\");\n return alternative;\n}\n\nasync function promptTeamOwner(currentUserEmail: string): Promise<string> {\n return await input({ message: \"Team owner email:\", default: currentUserEmail });\n}\n\nasync function maybeCommitWorkspace(workspacePath: string, autoYes?: boolean): Promise<void> {\n const wantCommit =\n autoYes ?? (await confirm({ message: \"Commit workspace ngay?\", default: true }));\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\");\n log.success(\"Đã commit workspace\");\n}\n\nfunction printInitSuccessBox(rootPath: string, flow: ProjectStatus): void {\n const lines: string[] = [\n `${chalk.green(\"✓\")} Workspace sẵn sàng: ${relative(process.cwd(), rootPath) || rootPath}`,\n ` ${chalk.dim(`(flow: ${flow})`)}`,\n \"\",\n ` ${chalk.cyan(`cd ${rootPath}`)}`,\n ` ${chalk.cyan(\"claude\")} Mở Claude Code ở workspace root`,\n \"\",\n ` ${chalk.cyan(\"avatar commit --src\")} Commit code lên remote src`,\n ` ${chalk.cyan(\"avatar commit --avatar\")} Commit Avatar state`,\n ` ${chalk.cyan(\"avatar sync\")} Pull team-ai-pack mới`,\n ` ${chalk.cyan(\"avatar uninstall\")} Gỡ Avatar (giữ code)`,\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","// Avatar ASCII banner — gradient màu cam → tím để in ở các entry point chính:\n// `avatar --version`, `avatar init`, `avatar login`.\n//\n// Style: ANSI Shadow block characters (Unicode box-drawing).\n// Tự fallback sang plain text khi không phải TTY (CI, pipe ra file).\nimport chalk from \"chalk\";\n\n// 6 dòng chính của logo, mỗi dòng giữ độ rộng đồng nhất 50 ký tự để gradient đẹp.\nconst BANNER_LINES: readonly string[] = [\n \" █████╗ ██╗ ██╗ █████╗ ████████╗ █████╗ ██████╗ \",\n \"██╔══██╗██║ ██║██╔══██╗╚══██╔══╝██╔══██╗██╔══██╗\",\n \"███████║██║ ██║███████║ ██║ ███████║██████╔╝\",\n \"██╔══██║╚██╗ ██╔╝██╔══██║ ██║ ██╔══██║██╔══██╗\",\n \"██║ ██║ ╚████╔╝ ██║ ██║ ██║ ██║ ██║██║ ██║\",\n \"╚═╝ ╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝\",\n] as const;\n\n// Gradient stops cam → tím (RGB triples). Mỗi dòng nội suy theo % chiều cao.\nconst GRADIENT_STOPS: ReadonlyArray<readonly [number, number, number]> = [\n [217, 79, 30], // cam-cháy (#d94f1e)\n [200, 70, 80], // cam-hồng\n [170, 70, 140], // hồng-tím\n [125, 88, 217], // tím (#7d58d9)\n];\n\n// Nội suy tuyến tính 1 kênh màu giữa 2 stop.\nfunction lerpChannel(a: number, b: number, t: number): number {\n return Math.round(a + (b - a) * t);\n}\n\n// Lấy màu RGB tại vị trí [0,1] trên gradient.\nfunction gradientAt(t: number): readonly [number, number, number] {\n const clamped = Math.max(0, Math.min(1, t));\n const scaled = clamped * (GRADIENT_STOPS.length - 1);\n const lo = Math.floor(scaled);\n const hi = Math.min(GRADIENT_STOPS.length - 1, lo + 1);\n const localT = scaled - lo;\n const a = GRADIENT_STOPS[lo];\n const b = GRADIENT_STOPS[hi];\n return [\n lerpChannel(a[0], b[0], localT),\n lerpChannel(a[1], b[1], localT),\n lerpChannel(a[2], b[2], localT),\n ];\n}\n\n// Build banner string đã tô màu sẵn. Trả empty khi terminal không hỗ trợ màu.\nexport function renderAvatarBanner(opts?: { tagline?: string }): string {\n const isTty = process.stdout.isTTY ?? false;\n const supportsColor = isTty && chalk.level > 0;\n\n // Plain fallback cho CI hoặc pipe.\n if (!supportsColor) {\n return [...BANNER_LINES, ...(opts?.tagline ? [\"\", opts.tagline] : [])].join(\"\\n\");\n }\n\n const colored = BANNER_LINES.map((line, idx) => {\n const t = BANNER_LINES.length === 1 ? 0 : idx / (BANNER_LINES.length - 1);\n const [r, g, b] = gradientAt(t);\n return chalk.rgb(r, g, b).bold(line);\n });\n\n if (opts?.tagline) {\n colored.push(\"\");\n colored.push(chalk.dim(opts.tagline));\n }\n\n return colored.join(\"\\n\");\n}\n\n// Tiện ích: in banner ra stdout với newline phía trước/sau cho thoáng.\nexport function printAvatarBanner(opts?: { tagline?: string }): void {\n process.stdout.write(`\\n${renderAvatarBanner(opts)}\\n\\n`);\n}\n","// Wrapper around `gh repo create <org>/<name> --<vis> --source=<folder>\n// --remote=origin --push`. Stream stdio để user thấy gh prompt nếu có.\nimport { spawnSync } from \"node:child_process\";\nimport type { RepoVisibility } from \"./validate-repo-name-and-visibility.js\";\n\nexport class RepoAlreadyExistsError extends Error {\n constructor(fullName: string) {\n super(`Repo \"${fullName}\" đã tồn tại trên GitHub. Đổi tên hoặc xóa repo cũ.`);\n this.name = \"RepoAlreadyExistsError\";\n }\n}\n\nexport interface ExecuteGhRepoCreateInput {\n folder: string;\n org: string;\n name: string;\n visibility: RepoVisibility;\n}\n\nexport interface ExecuteGhRepoCreateOutput {\n sshUrl: string;\n httpsUrl: string;\n}\n\nexport function executeGhRepoCreate(input: ExecuteGhRepoCreateInput): ExecuteGhRepoCreateOutput {\n const fullName = `${input.org}/${input.name}`;\n const args = [\n \"repo\",\n \"create\",\n fullName,\n `--${input.visibility}`,\n \"--source\",\n input.folder,\n \"--remote\",\n \"origin\",\n \"--push\",\n ];\n const r = spawnSync(\"gh\", args, { stdio: \"inherit\" });\n if (r.status !== 0) {\n // gh thường in \"GraphQL: Name already exists\" cho duplicate. Không parse\n // stdout (vì inherit) — surface generic error, user sẽ thấy stderr của gh.\n if (r.status === 1) {\n throw new RepoAlreadyExistsError(fullName);\n }\n throw new Error(`gh repo create thất bại (exit ${r.status})`);\n }\n return {\n sshUrl: `git@github.com:${fullName}.git`,\n httpsUrl: `https://github.com/${fullName}.git`,\n };\n}\n","// Lấy GitHub login mặc định của user qua `gh api user --jq .login`.\n// Dùng làm default org khi user không truyền --repo-org.\nimport { spawnSync } from \"node:child_process\";\n\nexport function resolveGithubUsernameDefault(): string {\n const r = spawnSync(\"gh\", [\"api\", \"user\", \"--jq\", \".login\"], {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n if (r.status !== 0) {\n throw new Error(`Không lấy được GitHub username: ${r.stderr?.trim()}`);\n }\n return r.stdout.trim();\n}\n","// Validate repo name + visibility trước khi gọi `gh repo create`. Fail-fast với\n// thông báo Vietnamese rõ ràng. GitHub repo name spec: alphanumeric, dash,\n// underscore, dot. 1-100 chars.\nconst REPO_NAME_REGEX = /^[a-zA-Z0-9._-]{1,100}$/;\n\nexport type RepoVisibility = \"private\" | \"public\";\n\nexport class InvalidRepoNameError extends Error {\n constructor(name: string) {\n super(\n `Tên repo \"${name}\" không hợp lệ. Chỉ dùng chữ/số/dấu chấm/gạch/underscore, dài 1-100 ký tự.`,\n );\n this.name = \"InvalidRepoNameError\";\n }\n}\n\nexport function validateRepoName(name: string): void {\n if (!REPO_NAME_REGEX.test(name)) {\n throw new InvalidRepoNameError(name);\n }\n}\n\nexport function validateRepoVisibility(v: string): asserts v is RepoVisibility {\n if (v !== \"private\" && v !== \"public\") {\n throw new Error(`Visibility phải là \"private\" hoặc \"public\", nhận: \"${v}\"`);\n }\n}\n","// Orchestrator phase 4: validate input → gọi gh repo create → return URLs.\n// Caller chịu trách nhiệm đảm bảo gh đã auth (gọi ensureGitHubReady trước).\nimport { type ExecuteGhRepoCreateOutput, executeGhRepoCreate } from \"./execute-gh-repo-create.js\";\nimport { resolveGithubUsernameDefault } from \"./resolve-github-username-default.js\";\nimport { log } from \"./terminal-logger.js\";\nimport {\n type RepoVisibility,\n validateRepoName,\n validateRepoVisibility,\n} from \"./validate-repo-name-and-visibility.js\";\n\nexport interface CreateGithubRemoteInput {\n folder: string;\n name: string;\n visibility: RepoVisibility;\n org?: string; // mặc định = GitHub login của user\n}\n\nexport function createGithubRemoteFromFolder(\n input: CreateGithubRemoteInput,\n): ExecuteGhRepoCreateOutput {\n validateRepoName(input.name);\n validateRepoVisibility(input.visibility);\n\n const org = input.org ?? resolveGithubUsernameDefault();\n log.info(`Tạo GitHub repo ${org}/${input.name} (${input.visibility})...`);\n\n const urls = executeGhRepoCreate({\n folder: input.folder,\n org,\n name: input.name,\n visibility: input.visibility,\n });\n\n log.success(`Đã tạo: ${urls.sshUrl}`);\n return urls;\n}\n","// Kiểm tra gh CLI đã login chưa. `gh auth status` exit 0 = OK.\n// Không parse stdout vì format thay đổi giữa các bản gh.\nimport { spawnSync } from \"node:child_process\";\n\nexport type GhAuthState = \"not-installed\" | \"not-authenticated\" | \"authenticated\";\n\nexport function checkGhCliAuthStatus(): GhAuthState {\n // Probe binary trước. spawnSync với gh không tồn tại trả ENOENT.\n const r = spawnSync(\"gh\", [\"auth\", \"status\"], { stdio: \"ignore\" });\n if (r.error && (r.error as NodeJS.ErrnoException).code === \"ENOENT\") {\n return \"not-installed\";\n }\n return r.status === 0 ? \"authenticated\" : \"not-authenticated\";\n}\n","// Detect package manager để cài binary hệ thống (gh CLI). Order matters:\n// brew (macOS preferred) → winget (Windows) → apt/dnf/pacman (Linux distros).\n// Returns null nếu không tìm được PM nào — caller phải fail-fast.\nimport { spawnSync } from \"node:child_process\";\nimport { detectHostPlatform } from \"./detect-host-platform.js\";\n\nexport type PackageManager = \"brew\" | \"apt\" | \"dnf\" | \"pacman\" | \"winget\";\n\n// Probe binary có trong PATH không. Dùng `command -v` (POSIX) hoặc `where` (Win).\nfunction hasBinary(name: string): boolean {\n const platform = detectHostPlatform();\n const probe = platform === \"win32\" ? \"where\" : \"command\";\n const args = platform === \"win32\" ? [name] : [\"-v\", name];\n // spawnSync với shell=true để `command -v` (builtin) hoạt động.\n const r = spawnSync(probe, args, {\n shell: platform !== \"win32\",\n stdio: \"ignore\",\n });\n return r.status === 0;\n}\n\n// Trả về PM đầu tiên có sẵn theo thứ tự ưu tiên phù hợp với OS.\nexport function detectPackageManager(): PackageManager | null {\n const platform = detectHostPlatform();\n const candidates: PackageManager[] =\n platform === \"darwin\"\n ? [\"brew\"]\n : platform === \"win32\"\n ? [\"winget\"]\n : platform === \"linux\"\n ? [\"apt\", \"dnf\", \"pacman\"]\n : [];\n for (const pm of candidates) {\n if (hasBinary(pm)) return pm;\n }\n return null;\n}\n","// Wrapper mỏng quanh os.platform() để các module khác switch theo OS dễ hơn\n// và test dễ mock.\nimport { platform } from \"node:os\";\n\nexport type HostPlatform = \"darwin\" | \"linux\" | \"win32\" | \"unsupported\";\n\n// Trả về tên platform chuẩn hoá. Mọi giá trị khác 3 OS chính → \"unsupported\".\nexport function detectHostPlatform(): HostPlatform {\n const p = platform();\n if (p === \"darwin\" || p === \"linux\" || p === \"win32\") return p;\n return \"unsupported\";\n}\n","// Cài `gh` CLI qua package manager đã detect. Stream stdio để user thấy progress.\n// Throws nếu cài fail hoặc PM trả non-zero.\nimport { spawnSync } from \"node:child_process\";\nimport type { PackageManager } from \"./detect-package-manager.js\";\nimport { log } from \"./terminal-logger.js\";\n\n// Map PM → command + args để cài gh. apt/dnf cần sudo; brew/pacman/winget thì không.\nconst INSTALL_COMMANDS: Record<PackageManager, { cmd: string; args: string[] }> = {\n brew: { cmd: \"brew\", args: [\"install\", \"gh\"] },\n apt: { cmd: \"sudo\", args: [\"apt-get\", \"install\", \"-y\", \"gh\"] },\n dnf: { cmd: \"sudo\", args: [\"dnf\", \"install\", \"-y\", \"gh\"] },\n pacman: { cmd: \"sudo\", args: [\"pacman\", \"-S\", \"--noconfirm\", \"github-cli\"] },\n winget: { cmd: \"winget\", args: [\"install\", \"--id\", \"GitHub.cli\", \"-e\", \"--silent\"] },\n};\n\nexport function installGhCliViaPackageManager(pm: PackageManager): void {\n const spec = INSTALL_COMMANDS[pm];\n log.info(`Đang cài gh CLI qua ${pm}...`);\n const r = spawnSync(spec.cmd, spec.args, { stdio: \"inherit\" });\n if (r.status !== 0) {\n throw new Error(`Cài gh CLI thất bại qua ${pm} (exit ${r.status}). Cài tay rồi chạy lại.`);\n }\n log.success(\"Đã cài gh CLI\");\n}\n","// Config git credential helper dùng gh token. Cần thiết để `git ls-remote`,\n// `git clone`, `git push` qua HTTPS hoạt động khi gh đã auth nhưng git chưa\n// biết về token đó. Idempotent — chạy nhiều lần OK.\nimport { spawnSync } from \"node:child_process\";\nimport { log } from \"./terminal-logger.js\";\n\nexport function setupGitCredentialViaGh(): void {\n const r = spawnSync(\"gh\", [\"auth\", \"setup-git\"], { stdio: \"ignore\" });\n if (r.status !== 0) {\n // Không throw — nếu setup-git fail, git operation sau có thể vẫn work\n // (vd user đã có credential helper khác). Chỉ warn.\n log.warn(\"gh auth setup-git fail (non-fatal). Nếu git clone lỗi 128 → chạy thủ công.\");\n return;\n }\n log.dim(\"Git credential helper đã link với gh token.\");\n}\n","// Spawn `gh auth login --hostname github.com --web` interactive.\n// User sẽ thấy device-code prompt và browser tự mở. Block đến khi xong.\nimport { spawnSync } from \"node:child_process\";\nimport { log } from \"./terminal-logger.js\";\n\nexport function triggerGhCliAuthLogin(): void {\n log.info(\"Khởi động đăng nhập GitHub qua gh CLI (browser sẽ mở)...\");\n const r = spawnSync(\n \"gh\",\n [\"auth\", \"login\", \"--hostname\", \"github.com\", \"--web\", \"--git-protocol\", \"ssh\"],\n { stdio: \"inherit\" },\n );\n if (r.status !== 0) {\n throw new Error(`gh auth login thất bại (exit ${r.status}). Thử 'gh auth login' tay.`);\n }\n log.success(\"Đã đăng nhập GitHub\");\n}\n","// Verify một remote URL có accessible không bằng `git ls-remote <url> HEAD`\n// với timeout 5 giây. Tách ra để init flow check sớm — fail-fast nếu URL sai.\nimport { spawnSync } from \"node:child_process\";\n\nconst TIMEOUT_MS = 5_000;\n\nexport class RemoteNotAccessibleError extends Error {\n constructor(url: string, reason: string) {\n super(`Không truy cập được remote ${url}: ${reason}`);\n this.name = \"RemoteNotAccessibleError\";\n }\n}\n\nexport function verifyGitRemoteAccessible(url: string): void {\n const r = spawnSync(\"git\", [\"ls-remote\", \"--exit-code\", url, \"HEAD\"], {\n stdio: \"ignore\",\n timeout: TIMEOUT_MS,\n });\n if (r.status === 0) return;\n // signal=SIGTERM nghĩa là Node.js kill do timeout.\n if (r.signal === \"SIGTERM\") throw new RemoteNotAccessibleError(url, \"timeout 5s\");\n throw new RemoteNotAccessibleError(url, `git ls-remote exit ${r.status}`);\n}\n","// Orchestrator phase 2: đảm bảo gh CLI có và đã auth. Tự cài + tự login nếu cần.\n// Chỉ throws khi không thể tự fix (PM thiếu, login thất bại).\nimport { checkGhCliAuthStatus } from \"./check-gh-cli-auth-status.js\";\nimport { detectPackageManager } from \"./detect-package-manager.js\";\nimport { installGhCliViaPackageManager } from \"./install-gh-cli-via-package-manager.js\";\nimport { setupGitCredentialViaGh } from \"./setup-git-credential-via-gh.js\";\nimport { log } from \"./terminal-logger.js\";\nimport { triggerGhCliAuthLogin } from \"./trigger-gh-cli-auth-login.js\";\nimport { verifyGitRemoteAccessible } from \"./verify-git-remote-accessible.js\";\n\n// Gọi trước mọi flow cần GitHub (init nhánh 1, nhánh 2-create-remote, nhánh 3).\n// remoteUrl optional — nếu truyền sẽ verify access cụ thể.\nexport async function ensureGitHubReady(remoteUrl?: string): Promise<void> {\n let state = checkGhCliAuthStatus();\n\n if (state === \"not-installed\") {\n log.warn(\"gh CLI chưa cài. Avatar sẽ tự cài.\");\n const pm = detectPackageManager();\n if (!pm) {\n throw new Error(\n \"Không phát hiện package manager (brew/apt/dnf/pacman/winget). Cài gh CLI tay rồi chạy lại: https://cli.github.com\",\n );\n }\n installGhCliViaPackageManager(pm);\n state = checkGhCliAuthStatus();\n }\n\n if (state === \"not-authenticated\") {\n log.warn(\"Chưa đăng nhập GitHub.\");\n triggerGhCliAuthLogin();\n state = checkGhCliAuthStatus();\n if (state !== \"authenticated\") {\n throw new Error(\"Sau gh auth login vẫn chưa authenticated. Thử lại.\");\n }\n }\n\n log.success(\"gh CLI sẵn sàng\");\n\n // Đảm bảo git CLI dùng gh token cho HTTPS operations. Idempotent — chạy\n // mỗi lần init OK. Fix lỗi `git ls-remote exit 128` khi gh đã auth nhưng\n // git chưa biết.\n setupGitCredentialViaGh();\n\n if (remoteUrl) {\n verifyGitRemoteAccessible(remoteUrl);\n log.success(`Remote accessible: ${remoteUrl}`);\n }\n}\n","// Kiểm tra folder đã là git repo chưa. Đơn giản: check tồn tại của \".git\"\n// (folder hoặc file gitlink — cả hai đều hợp lệ với submodule).\nimport { existsSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nexport function checkFolderHasGit(folderPath: string): boolean {\n const gitPath = join(folderPath, \".git\");\n if (!existsSync(gitPath)) return false;\n // .git có thể là directory (repo bình thường) hoặc file (submodule gitlink).\n // Cả 2 đều xem là \"đã có git\".\n const stat = statSync(gitPath);\n return stat.isDirectory() || stat.isFile();\n}\n","// Init git repo trong folder + tạo initial commit. Idempotent: skip nếu đã\n// có commit. Default branch \"main\".\nimport { simpleGit } from \"simple-git\";\n\nconst INITIAL_COMMIT_MESSAGE = \"chore: initial commit\";\n\nexport async function createInitialGitCommit(folderPath: string): Promise<void> {\n const g = simpleGit({ baseDir: folderPath });\n\n // Init nếu chưa có .git.\n const isRepo = await g.checkIsRepo().catch(() => false);\n if (!isRepo) {\n await g.init();\n }\n\n // Đảm bảo branch hiện tại là main (đổi master → main nếu cần).\n // Một số git config user có init.defaultBranch=master.\n try {\n await g.branch([\"-M\", \"main\"]);\n } catch {\n // Repo trống chưa có commit nào — branch -M sẽ fail. Bỏ qua, commit\n // đầu tiên dưới đây sẽ tạo branch main qua HEAD ref.\n }\n\n // Stage all + commit. Nếu không có file (folder rỗng) thì commit empty để\n // submodule add có HEAD reference dùng.\n await g.add(\".\");\n const status = await g.status();\n const hasCommits = (await g.raw([\"rev-list\", \"-n\", \"1\", \"--all\"]).catch(() => \"\")).trim();\n if (hasCommits) return; // Đã có commit, skip.\n\n if (status.files.length === 0) {\n await g.commit(INITIAL_COMMIT_MESSAGE, undefined, { \"--allow-empty\": null });\n } else {\n await g.commit(INITIAL_COMMIT_MESSAGE);\n }\n}\n","// Detect tech stack của folder qua signature file ở root. Trả về tất cả stack\n// match được (folder polyglot là chuyện thường — vd monorepo Node + Python).\n// Nếu không match gì → [\"generic\"].\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nexport type TechStack = \"node\" | \"python\" | \"go\" | \"rust\" | \"java\" | \"ruby\" | \"generic\";\n\n// Bảng signature: stack → các file đủ điều kiện claim stack đó.\nconst SIGNATURES: Record<Exclude<TechStack, \"generic\">, string[]> = {\n node: [\"package.json\"],\n python: [\"pyproject.toml\", \"requirements.txt\", \"setup.py\", \"Pipfile\"],\n go: [\"go.mod\"],\n rust: [\"Cargo.toml\"],\n java: [\"pom.xml\", \"build.gradle\", \"build.gradle.kts\"],\n ruby: [\"Gemfile\"],\n};\n\nexport function detectFolderTechStack(folderPath: string): TechStack[] {\n const matched: TechStack[] = [];\n for (const [stack, files] of Object.entries(SIGNATURES) as [\n Exclude<TechStack, \"generic\">,\n string[],\n ][]) {\n if (files.some((f) => existsSync(join(folderPath, f)))) {\n matched.push(stack);\n }\n }\n return matched.length > 0 ? matched : [\"generic\"];\n}\n","// Load template gitignore từ src/templates/gitignore/<stack>.txt và compose\n// nội dung tổng hợp cho 1+ stack. Generic luôn được prepend.\nimport { readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { TechStack } from \"./detect-folder-tech-stack.js\";\n\n// Resolve template dir tương đối với file này, không phải CWD. Khi bundle bằng\n// tsup, file này nằm trong dist/, templates nằm trong src/templates — tsup copy\n// templates qua --publicDir (đã cấu hình trong tsup.config.ts) hoặc resolve\n// qua relative path.\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\n// Thứ tự search: relative tới file build (dist/), fallback dev (src/lib/).\nconst CANDIDATE_DIRS = [\n join(__dirname, \"..\", \"templates\", \"gitignore\"),\n join(__dirname, \"..\", \"..\", \"src\", \"templates\", \"gitignore\"),\n];\n\nconst AVATAR_MARKER_START = \"# === avatar ===\";\nconst AVATAR_MARKER_END = \"# === /avatar ===\";\n\nfunction readTemplate(stack: TechStack): string {\n for (const dir of CANDIDATE_DIRS) {\n try {\n return readFileSync(join(dir, `${stack}.txt`), \"utf8\");\n } catch {\n // continue\n }\n }\n throw new Error(`Không tìm thấy template gitignore cho stack \"${stack}\"`);\n}\n\n// Compose: generic luôn ở đầu, sau đó các stack khác theo thứ tự detect.\n// Wrap trong marker để uninstall biết range gỡ chính xác.\nexport function composeGitignoreContent(stacks: TechStack[]): string {\n const all: TechStack[] = [\"generic\", ...stacks.filter((s) => s !== \"generic\")];\n const sections = all.map((s) => `# --- ${s} ---\\n${readTemplate(s).trim()}`);\n return [AVATAR_MARKER_START, ...sections, AVATAR_MARKER_END, \"\"].join(\"\\n\");\n}\n\nexport { AVATAR_MARKER_START, AVATAR_MARKER_END };\n","// Ghi .gitignore: tạo mới nếu chưa có, merge content nếu đã có. Merge logic\n// dùng marker `# === avatar === ... # === /avatar ===`: replace block đó nếu\n// tồn tại, append nếu chưa.\nimport { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { AVATAR_MARKER_END, AVATAR_MARKER_START } from \"./gitignore-template-loader.js\";\n\nexport function writeOrMergeGitignore(folderPath: string, avatarBlock: string): void {\n const path = join(folderPath, \".gitignore\");\n\n if (!existsSync(path)) {\n writeFileSync(path, avatarBlock, \"utf8\");\n return;\n }\n\n const existing = readFileSync(path, \"utf8\");\n const startIdx = existing.indexOf(AVATAR_MARKER_START);\n const endIdx = existing.indexOf(AVATAR_MARKER_END);\n\n // Đã có marker → replace block giữa marker (giữ content trước/sau).\n if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {\n const before = existing.slice(0, startIdx);\n const after = existing.slice(endIdx + AVATAR_MARKER_END.length);\n writeFileSync(path, `${before.trimEnd()}\\n\\n${avatarBlock}${after.trimStart()}`, \"utf8\");\n return;\n }\n\n // Chưa có marker → append phía cuối, thêm newline phân cách.\n writeFileSync(path, `${existing.trimEnd()}\\n\\n${avatarBlock}`, \"utf8\");\n}\n","// Orchestrator phase 3: bootstrap git cho folder local. Idempotent — chạy trên\n// folder đã có git sẽ no-op (chỉ ensure .gitignore có Avatar block).\nimport { checkFolderHasGit } from \"./check-folder-has-git.js\";\nimport { createInitialGitCommit } from \"./create-initial-git-commit.js\";\nimport { detectFolderTechStack } from \"./detect-folder-tech-stack.js\";\nimport { composeGitignoreContent } from \"./gitignore-template-loader.js\";\nimport { log } from \"./terminal-logger.js\";\nimport { writeOrMergeGitignore } from \"./write-or-merge-gitignore.js\";\n\nexport async function bootstrapGitInFolder(folderPath: string): Promise<void> {\n const hadGit = checkFolderHasGit(folderPath);\n\n // Auto detect tech stack + write .gitignore với Avatar block.\n const stacks = detectFolderTechStack(folderPath);\n log.info(`Tech stack detected: ${stacks.join(\", \")}`);\n writeOrMergeGitignore(folderPath, composeGitignoreContent(stacks));\n log.success(\".gitignore đã ghi (Avatar block)\");\n\n if (!hadGit) {\n log.info(`Bootstrap git cho ${folderPath}...`);\n await createInitialGitCommit(folderPath);\n log.success(\"Đã git init + initial commit\");\n } else {\n log.dim(\"Folder đã có .git — skip init.\");\n }\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 { printAvatarBanner } from \"../lib/avatar-ascii-banner.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 // Banner trước khi vào device-code flow để user nhận diện thương hiệu.\n printAvatarBanner({ tagline: \"Đăng nhập Google SSO · workspace @nal.vn\" });\n\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","// `avatar uninstall` — Command 13 (v1.1).\n// Gỡ Avatar khỏi project + auto-backup vào ~/.avatar/uninstall-backups/.\n// Đối ứng với `avatar init`. Code khách (src/) giữ nguyên.\n\nimport { relative } from \"node:path\";\nimport { confirm } from \"@inquirer/prompts\";\nimport boxen from \"boxen\";\nimport type { Command } from \"commander\";\nimport { appendAuditEntry } from \"../lib/audit-log-appender.js\";\nimport { createUninstallBackupSnapshot } from \"../lib/create-uninstall-backup-snapshot.js\";\nimport { detectAvatarProjectArtifacts } from \"../lib/detect-avatar-project-artifacts.js\";\nimport { executeUninstallDeletion } from \"../lib/execute-uninstall-deletion.js\";\nimport { chalk, log } from \"../lib/terminal-logger.js\";\n\nconst CLI_VERSION = \"1.1.2\";\n\ninterface UninstallOptions {\n yes?: boolean;\n noBackup?: boolean;\n keepSubmodule?: boolean;\n keepHooks?: boolean;\n dryRun?: boolean;\n}\n\nexport function registerUninstallCommand(program: Command): void {\n program\n .command(\"uninstall\")\n .description(\"Gỡ Avatar khỏi project — backup tự động (M11)\")\n .option(\"--yes\", \"Skip confirm prompt\")\n .option(\"--no-backup\", \"Không tạo backup trước khi xóa (nguy hiểm)\")\n .option(\"--keep-submodule\", \"Giữ submodule .claude/pack/\")\n .option(\"--keep-hooks\", \"Giữ git hooks post-merge, pre-push\")\n .option(\"--dry-run\", \"Hiển thị danh sách sẽ xóa, không thực thi\")\n .action(async (opts: UninstallOptions) => {\n try {\n await runUninstall(opts);\n } catch (err) {\n log.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n });\n}\n\nasync function runUninstall(opts: UninstallOptions): Promise<void> {\n const projectRoot = process.cwd();\n const artifacts = detectAvatarProjectArtifacts(projectRoot);\n\n if (!artifacts.hasAnyArtifact) {\n log.info(\"Project chưa cài Avatar — không có gì để gỡ.\");\n return;\n }\n\n // Show summary.\n printUninstallSummary(projectRoot, artifacts, opts);\n\n if (opts.dryRun) {\n log.dim(\"--dry-run: kết thúc, không xóa.\");\n return;\n }\n\n // Confirm.\n if (!opts.yes) {\n const ok = await confirm({\n message: \"Tiếp tục gỡ Avatar?\",\n default: false,\n });\n if (!ok) {\n log.info(\"Đã hủy.\");\n return;\n }\n }\n\n // Backup (trừ khi --no-backup).\n let backupPath: string | null = null;\n if (!opts.noBackup) {\n backupPath = await createUninstallBackupSnapshot(projectRoot, artifacts, CLI_VERSION);\n log.success(`Backup tạo tại: ${backupPath}`);\n }\n\n // Delete artifacts.\n await executeUninstallDeletion(artifacts, {\n keepSubmodule: opts.keepSubmodule,\n keepHooks: opts.keepHooks,\n });\n\n await appendAuditEntry(\"uninstall\", `project=${projectRoot},backup=${backupPath ?? \"skipped\"}`);\n\n printUninstallSuccessBox(backupPath);\n}\n\nfunction printUninstallSummary(\n projectRoot: string,\n artifacts: ReturnType<typeof detectAvatarProjectArtifacts>,\n opts: UninstallOptions,\n): void {\n log.info(`Project: ${projectRoot}`);\n log.plain(\"\");\n log.plain(\"Các artifact sẽ gỡ:\");\n if (artifacts.claudeDir)\n log.plain(` ${chalk.red(\"✗\")} ${relative(projectRoot, artifacts.claudeDir) || \".claude/\"}`);\n if (artifacts.claudeMd) log.plain(` ${chalk.red(\"✗\")} CLAUDE.md`);\n if (artifacts.postMergeHook && !opts.keepHooks) {\n log.plain(` ${chalk.red(\"✗\")} .git/hooks/post-merge`);\n }\n if (artifacts.prePushHook && !opts.keepHooks) {\n log.plain(` ${chalk.red(\"✗\")} .git/modules/src/hooks/pre-push`);\n }\n if (artifacts.gitignorePath) log.plain(` ${chalk.yellow(\"✎\")} .gitignore (gỡ Avatar block)`);\n if (artifacts.gitmodulesPath && !opts.keepSubmodule) {\n log.plain(` ${chalk.yellow(\"✎\")} .gitmodules (gỡ entry .claude/pack)`);\n }\n log.plain(\"\");\n log.plain(\"Không đụng:\");\n log.plain(` ${chalk.green(\"✓\")} src/ (code khách)`);\n log.plain(` ${chalk.green(\"✓\")} Git history`);\n log.plain(` ${chalk.green(\"✓\")} ~/.avatar/config.json (token SSO)`);\n log.plain(` ${chalk.green(\"✓\")} Secrets trong keychain`);\n log.plain(\"\");\n}\n\nfunction printUninstallSuccessBox(backupPath: string | null): void {\n const lines: string[] = [`${chalk.green(\"✓\")} Avatar đã được gỡ khỏi project`];\n if (backupPath) {\n lines.push(\"\");\n lines.push(` ${chalk.dim(\"Backup:\")} ${backupPath}`);\n lines.push(` ${chalk.dim(\"Restore:\")} ${chalk.cyan(`cp -r \"${backupPath}\"/* .`)}`);\n }\n process.stdout.write(`${boxen(lines.join(\"\\n\"), { padding: 1, borderStyle: \"round\" })}\\n`);\n}\n","// Tạo snapshot backup trước khi uninstall. Folder: ~/.avatar/uninstall-backups/\n// <project-name>-<ts>/ với cấu trúc trong spec doc.\nimport { cp, mkdir, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { basename, join } from \"node:path\";\nimport type { AvatarProjectArtifacts } from \"./detect-avatar-project-artifacts.js\";\n\nexport interface BackupManifest {\n projectName: string;\n projectPath: string;\n timestamp: string;\n avatarVersion: string;\n artifacts: {\n claudeDir: boolean;\n claudeMd: boolean;\n postMergeHook: boolean;\n prePushHook: boolean;\n };\n}\n\nconst UNINSTALL_BACKUPS_DIR = join(homedir(), \".avatar\", \"uninstall-backups\");\n\nexport async function createUninstallBackupSnapshot(\n projectRoot: string,\n artifacts: AvatarProjectArtifacts,\n avatarVersion: string,\n): Promise<string> {\n const projectName = basename(projectRoot);\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n const backupDir = join(UNINSTALL_BACKUPS_DIR, `${projectName}-${timestamp}`);\n\n await mkdir(backupDir, { recursive: true, mode: 0o700 });\n\n // Copy .claude/ và CLAUDE.md nếu tồn tại.\n if (artifacts.claudeDir) {\n await cp(artifacts.claudeDir, join(backupDir, \".claude\"), { recursive: true });\n }\n if (artifacts.claudeMd) {\n await cp(artifacts.claudeMd, join(backupDir, \"CLAUDE.md\"));\n }\n\n // Copy hooks sang backup/hooks/.\n if (artifacts.postMergeHook || artifacts.prePushHook) {\n const hooksBackupDir = join(backupDir, \"hooks\");\n await mkdir(hooksBackupDir, { recursive: true });\n if (artifacts.postMergeHook) {\n await cp(artifacts.postMergeHook, join(hooksBackupDir, \"post-merge\"));\n }\n if (artifacts.prePushHook) {\n await cp(artifacts.prePushHook, join(hooksBackupDir, \"pre-push\"));\n }\n }\n\n // Write manifest.\n const manifest: BackupManifest = {\n projectName,\n projectPath: projectRoot,\n timestamp,\n avatarVersion,\n artifacts: {\n claudeDir: !!artifacts.claudeDir,\n claudeMd: !!artifacts.claudeMd,\n postMergeHook: !!artifacts.postMergeHook,\n prePushHook: !!artifacts.prePushHook,\n },\n };\n await writeFile(join(backupDir, \"manifest.json\"), JSON.stringify(manifest, null, 2), \"utf8\");\n\n return backupDir;\n}\n","// Scan project root để liệt kê các file/folder Avatar đã tạo. Output là blueprint\n// cho uninstall: cái gì sẽ xóa + cái gì sẽ edit (gitignore, gitmodules).\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nexport interface AvatarProjectArtifacts {\n hasAnyArtifact: boolean;\n claudeDir: string | null; // .claude/\n claudeMd: string | null; // CLAUDE.md\n postMergeHook: string | null; // .git/hooks/post-merge\n prePushHook: string | null; // .git/modules/src/hooks/pre-push\n gitignorePath: string | null; // .gitignore (nếu có Avatar block)\n gitmodulesPath: string | null; // .gitmodules (nếu có submodule .claude/pack)\n notesDir: string | null; // notes/ (workspace mode)\n scriptsDir: string | null; // scripts/ (workspace mode)\n}\n\nfunction existsOrNull(path: string): string | null {\n return existsSync(path) ? path : null;\n}\n\nexport function detectAvatarProjectArtifacts(projectRoot: string): AvatarProjectArtifacts {\n const claudeDir = existsOrNull(join(projectRoot, \".claude\"));\n const claudeMd = existsOrNull(join(projectRoot, \"CLAUDE.md\"));\n const postMergeHook = existsOrNull(join(projectRoot, \".git\", \"hooks\", \"post-merge\"));\n const prePushHook = existsOrNull(\n join(projectRoot, \".git\", \"modules\", \"src\", \"hooks\", \"pre-push\"),\n );\n const gitignorePath = existsOrNull(join(projectRoot, \".gitignore\"));\n const gitmodulesPath = existsOrNull(join(projectRoot, \".gitmodules\"));\n const notesDir = existsOrNull(join(projectRoot, \"notes\"));\n const scriptsDir = existsOrNull(join(projectRoot, \"scripts\"));\n\n const hasAnyArtifact = !!(claudeDir || claudeMd || postMergeHook || prePushHook);\n\n return {\n hasAnyArtifact,\n claudeDir,\n claudeMd,\n postMergeHook,\n prePushHook,\n gitignorePath,\n gitmodulesPath,\n notesDir,\n scriptsDir,\n };\n}\n","// Atomic delete các artifact Avatar khỏi project. Gỡ marker block trong\n// .gitignore, remove submodule entry trong .gitmodules. Không đụng src/ + git\n// history + user config.\nimport { readFile, rm, writeFile } from \"node:fs/promises\";\nimport type { AvatarProjectArtifacts } from \"./detect-avatar-project-artifacts.js\";\nimport { AVATAR_MARKER_END, AVATAR_MARKER_START } from \"./gitignore-template-loader.js\";\n\nexport interface UninstallFlags {\n keepSubmodule?: boolean;\n keepHooks?: boolean;\n}\n\nexport async function executeUninstallDeletion(\n artifacts: AvatarProjectArtifacts,\n flags: UninstallFlags,\n): Promise<void> {\n // Delete .claude/ (trừ khi --keep-submodule muốn giữ pack/ — thực tế cả\n // .claude/ chứa nhiều thứ khác, nên --keep-submodule chỉ giữ pack/).\n if (artifacts.claudeDir) {\n if (flags.keepSubmodule) {\n // Chỉ xóa các file/folder không phải pack/ trong .claude/.\n const { readdir } = await import(\"node:fs/promises\");\n const { join } = await import(\"node:path\");\n const entries = await readdir(artifacts.claudeDir);\n for (const entry of entries) {\n if (entry === \"pack\") continue;\n await rm(join(artifacts.claudeDir, entry), { recursive: true, force: true });\n }\n } else {\n await rm(artifacts.claudeDir, { recursive: true, force: true });\n }\n }\n\n if (artifacts.claudeMd) {\n await rm(artifacts.claudeMd, { force: true });\n }\n\n if (!flags.keepHooks) {\n if (artifacts.postMergeHook) await rm(artifacts.postMergeHook, { force: true });\n if (artifacts.prePushHook) await rm(artifacts.prePushHook, { force: true });\n }\n\n // Strip Avatar block khỏi .gitignore (giữ rest).\n if (artifacts.gitignorePath) {\n await stripAvatarBlockFromGitignore(artifacts.gitignorePath);\n }\n\n // Remove submodule entry .claude/pack khỏi .gitmodules (nếu xóa cả pack).\n if (artifacts.gitmodulesPath && !flags.keepSubmodule) {\n await removeSubmoduleEntry(artifacts.gitmodulesPath, \".claude/pack\");\n }\n\n // Workspace mode có notes/, scripts/. Chỉ xóa nếu rỗng (user có thể đã add file).\n for (const dir of [artifacts.notesDir, artifacts.scriptsDir]) {\n if (!dir) continue;\n const { readdir } = await import(\"node:fs/promises\");\n const entries = await readdir(dir);\n if (entries.length === 0) {\n await rm(dir, { recursive: true, force: true });\n }\n }\n}\n\nasync function stripAvatarBlockFromGitignore(path: string): Promise<void> {\n const content = await readFile(path, \"utf8\");\n const startIdx = content.indexOf(AVATAR_MARKER_START);\n const endIdx = content.indexOf(AVATAR_MARKER_END);\n if (startIdx === -1 || endIdx === -1) return;\n\n const before = content.slice(0, startIdx);\n const after = content.slice(endIdx + AVATAR_MARKER_END.length);\n const cleaned = `${before.trimEnd()}\\n${after.trimStart()}`.trim();\n if (cleaned.length === 0) {\n await rm(path, { force: true });\n } else {\n await writeFile(path, `${cleaned}\\n`, \"utf8\");\n }\n}\n\nasync function removeSubmoduleEntry(gitmodulesPath: string, submodulePath: string): Promise<void> {\n const content = await readFile(gitmodulesPath, \"utf8\");\n const lines = content.split(\"\\n\");\n const result: string[] = [];\n let skip = false;\n for (const line of lines) {\n if (line.trim().startsWith(\"[submodule\") && line.includes(submodulePath)) {\n skip = true;\n continue;\n }\n if (skip && line.trim().startsWith(\"[submodule\")) {\n skip = false;\n }\n if (!skip) result.push(line);\n }\n const cleaned = result.join(\"\\n\").trim();\n if (cleaned.length === 0) {\n await rm(gitmodulesPath, { force: true });\n } else {\n await writeFile(gitmodulesPath, `${cleaned}\\n`, \"utf8\");\n }\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","chalk","spawnSync","input","spawnSync","input","spawnSync","spawnSync","platform","spawnSync","spawnSync","spawnSync","spawnSync","spawnSync","spawnSync","spawnSync","spawnSync","existsSync","join","simpleGit","existsSync","join","dirname","join","fileURLToPath","existsSync","readFileSync","join","join","existsSync","readFileSync","join","join","join","join","program","join","relative","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","relative","confirm","boxen","homedir","basename","join","existsSync","join","writeFile","readdir","join","writeFile","program","confirm","relative","boxen","CLI_VERSION"]}
@@ -84,7 +84,7 @@ var COMMANDS = [
84
84
  { name: "uninstall", desc: "G\u1EE1 Avatar kh\u1ECFi project + backup" },
85
85
  { name: "help", desc: "Hi\u1EC3n th\u1ECB help cho t\u1EEBng l\u1EC7nh" }
86
86
  ];
87
- var CLI_VERSION = "1.1.1";
87
+ var CLI_VERSION = "1.1.2";
88
88
  function printWelcomeScreen() {
89
89
  printAvatarBanner({ tagline: `v${CLI_VERSION} \xB7 AI harness CLI for NAL Vietnam` });
90
90
  const maxNameWidth = Math.max(...COMMANDS.map((c) => c.name.length));
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/lib/print-welcome-screen.ts","../../src/lib/avatar-ascii-banner.ts","../../src/lib/terminal-logger.ts"],"sourcesContent":["// Welcome screen in ra sau `npm install -g` (qua postinstall hook) hoặc khi\n// user gọi `avatar welcome` thủ công. Mục đích: cho user thấy banner + 13\n// commands + next step gợi ý ngay sau khi cài.\nimport boxen from \"boxen\";\nimport { printAvatarBanner } from \"./avatar-ascii-banner.js\";\nimport { chalk } from \"./terminal-logger.js\";\n\ninterface CommandSummary {\n name: string;\n desc: string;\n}\n\n// Danh sách 13 commands user-facing (mcp-run là hidden, bỏ qua).\nconst COMMANDS: CommandSummary[] = [\n { name: \"login\", desc: \"Đăng nhập Google SSO (@nal.vn)\" },\n { name: \"init\", desc: \"Khởi tạo Avatar — 3 flow tự nhận diện\" },\n { name: \"sync\", desc: \"Pull team-ai-pack mới nhất\" },\n { name: \"scan\", desc: \"Project scanner + knowledge proposal\" },\n { name: \"review\", desc: \"Duyệt pending proposals\" },\n { name: \"status\", desc: \"Snapshot project, pack, pending, backup\" },\n { name: \"doctor\", desc: \"Chẩn đoán cài đặt Avatar\" },\n { name: \"restore\", desc: \"Khôi phục .claude/pack/ từ backup\" },\n { name: \"commit\", desc: \"Commit src/ hoặc Avatar state (client mode)\" },\n { name: \"tools\", desc: \"Quản lý system tools + MCP servers\" },\n { name: \"secrets\", desc: \"Quản lý secrets trong OS keychain\" },\n { name: \"uninstall\", desc: \"Gỡ Avatar khỏi project + backup\" },\n { name: \"help\", desc: \"Hiển thị help cho từng lệnh\" },\n];\n\nconst CLI_VERSION = \"1.1.1\";\n\nexport function printWelcomeScreen(): void {\n printAvatarBanner({ tagline: `v${CLI_VERSION} · AI harness CLI for NAL Vietnam` });\n\n // Tính độ rộng max của tên command để align cột.\n const maxNameWidth = Math.max(...COMMANDS.map((c) => c.name.length));\n\n const commandLines = COMMANDS.map((c) => {\n const padded = c.name.padEnd(maxNameWidth);\n return ` ${chalk.cyan(padded)} ${chalk.dim(c.desc)}`;\n }).join(\"\\n\");\n\n const nextSteps = [\n \"\",\n chalk.bold(\"13 lệnh sẵn sàng:\"),\n \"\",\n commandLines,\n \"\",\n chalk.bold(\"Next steps:\"),\n \"\",\n ` ${chalk.green(\"1.\")} ${chalk.cyan(\"avatar login\")} Đăng nhập SSO`,\n ` ${chalk.green(\"2.\")} ${chalk.cyan(\"avatar init\")} Khởi tạo dự án`,\n ` ${chalk.green(\"?.\")} ${chalk.cyan(\"avatar <command> --help\")} Xem chi tiết lệnh`,\n \"\",\n chalk.dim(\"Docs: https://www.npmjs.com/package/@nalvietnam/avatar-cli\"),\n ].join(\"\\n\");\n\n process.stdout.write(\n `${boxen(nextSteps, { padding: 1, borderStyle: \"round\", borderColor: \"magenta\" })}\\n`,\n );\n}\n\n// Caller (bin/postinstall.js) chịu trách nhiệm gọi printWelcomeScreen() —\n// không auto-execute ở đây để tránh in trùng khi module được import từ postinstall.\n","// Avatar ASCII banner — gradient màu cam → tím để in ở các entry point chính:\n// `avatar --version`, `avatar init`, `avatar login`.\n//\n// Style: ANSI Shadow block characters (Unicode box-drawing).\n// Tự fallback sang plain text khi không phải TTY (CI, pipe ra file).\nimport chalk from \"chalk\";\n\n// 6 dòng chính của logo, mỗi dòng giữ độ rộng đồng nhất 50 ký tự để gradient đẹp.\nconst BANNER_LINES: readonly string[] = [\n \" █████╗ ██╗ ██╗ █████╗ ████████╗ █████╗ ██████╗ \",\n \"██╔══██╗██║ ██║██╔══██╗╚══██╔══╝██╔══██╗██╔══██╗\",\n \"███████║██║ ██║███████║ ██║ ███████║██████╔╝\",\n \"██╔══██║╚██╗ ██╔╝██╔══██║ ██║ ██╔══██║██╔══██╗\",\n \"██║ ██║ ╚████╔╝ ██║ ██║ ██║ ██║ ██║██║ ██║\",\n \"╚═╝ ╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝\",\n] as const;\n\n// Gradient stops cam → tím (RGB triples). Mỗi dòng nội suy theo % chiều cao.\nconst GRADIENT_STOPS: ReadonlyArray<readonly [number, number, number]> = [\n [217, 79, 30], // cam-cháy (#d94f1e)\n [200, 70, 80], // cam-hồng\n [170, 70, 140], // hồng-tím\n [125, 88, 217], // tím (#7d58d9)\n];\n\n// Nội suy tuyến tính 1 kênh màu giữa 2 stop.\nfunction lerpChannel(a: number, b: number, t: number): number {\n return Math.round(a + (b - a) * t);\n}\n\n// Lấy màu RGB tại vị trí [0,1] trên gradient.\nfunction gradientAt(t: number): readonly [number, number, number] {\n const clamped = Math.max(0, Math.min(1, t));\n const scaled = clamped * (GRADIENT_STOPS.length - 1);\n const lo = Math.floor(scaled);\n const hi = Math.min(GRADIENT_STOPS.length - 1, lo + 1);\n const localT = scaled - lo;\n const a = GRADIENT_STOPS[lo];\n const b = GRADIENT_STOPS[hi];\n return [\n lerpChannel(a[0], b[0], localT),\n lerpChannel(a[1], b[1], localT),\n lerpChannel(a[2], b[2], localT),\n ];\n}\n\n// Build banner string đã tô màu sẵn. Trả empty khi terminal không hỗ trợ màu.\nexport function renderAvatarBanner(opts?: { tagline?: string }): string {\n const isTty = process.stdout.isTTY ?? false;\n const supportsColor = isTty && chalk.level > 0;\n\n // Plain fallback cho CI hoặc pipe.\n if (!supportsColor) {\n return [...BANNER_LINES, ...(opts?.tagline ? [\"\", opts.tagline] : [])].join(\"\\n\");\n }\n\n const colored = BANNER_LINES.map((line, idx) => {\n const t = BANNER_LINES.length === 1 ? 0 : idx / (BANNER_LINES.length - 1);\n const [r, g, b] = gradientAt(t);\n return chalk.rgb(r, g, b).bold(line);\n });\n\n if (opts?.tagline) {\n colored.push(\"\");\n colored.push(chalk.dim(opts.tagline));\n }\n\n return colored.join(\"\\n\");\n}\n\n// Tiện ích: in banner ra stdout với newline phía trước/sau cho thoáng.\nexport function printAvatarBanner(opts?: { tagline?: string }): void {\n process.stdout.write(`\\n${renderAvatarBanner(opts)}\\n\\n`);\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"],"mappings":"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","names":["chalk","chalk"]}
1
+ {"version":3,"sources":["../../src/lib/print-welcome-screen.ts","../../src/lib/avatar-ascii-banner.ts","../../src/lib/terminal-logger.ts"],"sourcesContent":["// Welcome screen in ra sau `npm install -g` (qua postinstall hook) hoặc khi\n// user gọi `avatar welcome` thủ công. Mục đích: cho user thấy banner + 13\n// commands + next step gợi ý ngay sau khi cài.\nimport boxen from \"boxen\";\nimport { printAvatarBanner } from \"./avatar-ascii-banner.js\";\nimport { chalk } from \"./terminal-logger.js\";\n\ninterface CommandSummary {\n name: string;\n desc: string;\n}\n\n// Danh sách 13 commands user-facing (mcp-run là hidden, bỏ qua).\nconst COMMANDS: CommandSummary[] = [\n { name: \"login\", desc: \"Đăng nhập Google SSO (@nal.vn)\" },\n { name: \"init\", desc: \"Khởi tạo Avatar — 3 flow tự nhận diện\" },\n { name: \"sync\", desc: \"Pull team-ai-pack mới nhất\" },\n { name: \"scan\", desc: \"Project scanner + knowledge proposal\" },\n { name: \"review\", desc: \"Duyệt pending proposals\" },\n { name: \"status\", desc: \"Snapshot project, pack, pending, backup\" },\n { name: \"doctor\", desc: \"Chẩn đoán cài đặt Avatar\" },\n { name: \"restore\", desc: \"Khôi phục .claude/pack/ từ backup\" },\n { name: \"commit\", desc: \"Commit src/ hoặc Avatar state (client mode)\" },\n { name: \"tools\", desc: \"Quản lý system tools + MCP servers\" },\n { name: \"secrets\", desc: \"Quản lý secrets trong OS keychain\" },\n { name: \"uninstall\", desc: \"Gỡ Avatar khỏi project + backup\" },\n { name: \"help\", desc: \"Hiển thị help cho từng lệnh\" },\n];\n\nconst CLI_VERSION = \"1.1.2\";\n\nexport function printWelcomeScreen(): void {\n printAvatarBanner({ tagline: `v${CLI_VERSION} · AI harness CLI for NAL Vietnam` });\n\n // Tính độ rộng max của tên command để align cột.\n const maxNameWidth = Math.max(...COMMANDS.map((c) => c.name.length));\n\n const commandLines = COMMANDS.map((c) => {\n const padded = c.name.padEnd(maxNameWidth);\n return ` ${chalk.cyan(padded)} ${chalk.dim(c.desc)}`;\n }).join(\"\\n\");\n\n const nextSteps = [\n \"\",\n chalk.bold(\"13 lệnh sẵn sàng:\"),\n \"\",\n commandLines,\n \"\",\n chalk.bold(\"Next steps:\"),\n \"\",\n ` ${chalk.green(\"1.\")} ${chalk.cyan(\"avatar login\")} Đăng nhập SSO`,\n ` ${chalk.green(\"2.\")} ${chalk.cyan(\"avatar init\")} Khởi tạo dự án`,\n ` ${chalk.green(\"?.\")} ${chalk.cyan(\"avatar <command> --help\")} Xem chi tiết lệnh`,\n \"\",\n chalk.dim(\"Docs: https://www.npmjs.com/package/@nalvietnam/avatar-cli\"),\n ].join(\"\\n\");\n\n process.stdout.write(\n `${boxen(nextSteps, { padding: 1, borderStyle: \"round\", borderColor: \"magenta\" })}\\n`,\n );\n}\n\n// Caller (bin/postinstall.js) chịu trách nhiệm gọi printWelcomeScreen() —\n// không auto-execute ở đây để tránh in trùng khi module được import từ postinstall.\n","// Avatar ASCII banner — gradient màu cam → tím để in ở các entry point chính:\n// `avatar --version`, `avatar init`, `avatar login`.\n//\n// Style: ANSI Shadow block characters (Unicode box-drawing).\n// Tự fallback sang plain text khi không phải TTY (CI, pipe ra file).\nimport chalk from \"chalk\";\n\n// 6 dòng chính của logo, mỗi dòng giữ độ rộng đồng nhất 50 ký tự để gradient đẹp.\nconst BANNER_LINES: readonly string[] = [\n \" █████╗ ██╗ ██╗ █████╗ ████████╗ █████╗ ██████╗ \",\n \"██╔══██╗██║ ██║██╔══██╗╚══██╔══╝██╔══██╗██╔══██╗\",\n \"███████║██║ ██║███████║ ██║ ███████║██████╔╝\",\n \"██╔══██║╚██╗ ██╔╝██╔══██║ ██║ ██╔══██║██╔══██╗\",\n \"██║ ██║ ╚████╔╝ ██║ ██║ ██║ ██║ ██║██║ ██║\",\n \"╚═╝ ╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝\",\n] as const;\n\n// Gradient stops cam → tím (RGB triples). Mỗi dòng nội suy theo % chiều cao.\nconst GRADIENT_STOPS: ReadonlyArray<readonly [number, number, number]> = [\n [217, 79, 30], // cam-cháy (#d94f1e)\n [200, 70, 80], // cam-hồng\n [170, 70, 140], // hồng-tím\n [125, 88, 217], // tím (#7d58d9)\n];\n\n// Nội suy tuyến tính 1 kênh màu giữa 2 stop.\nfunction lerpChannel(a: number, b: number, t: number): number {\n return Math.round(a + (b - a) * t);\n}\n\n// Lấy màu RGB tại vị trí [0,1] trên gradient.\nfunction gradientAt(t: number): readonly [number, number, number] {\n const clamped = Math.max(0, Math.min(1, t));\n const scaled = clamped * (GRADIENT_STOPS.length - 1);\n const lo = Math.floor(scaled);\n const hi = Math.min(GRADIENT_STOPS.length - 1, lo + 1);\n const localT = scaled - lo;\n const a = GRADIENT_STOPS[lo];\n const b = GRADIENT_STOPS[hi];\n return [\n lerpChannel(a[0], b[0], localT),\n lerpChannel(a[1], b[1], localT),\n lerpChannel(a[2], b[2], localT),\n ];\n}\n\n// Build banner string đã tô màu sẵn. Trả empty khi terminal không hỗ trợ màu.\nexport function renderAvatarBanner(opts?: { tagline?: string }): string {\n const isTty = process.stdout.isTTY ?? false;\n const supportsColor = isTty && chalk.level > 0;\n\n // Plain fallback cho CI hoặc pipe.\n if (!supportsColor) {\n return [...BANNER_LINES, ...(opts?.tagline ? [\"\", opts.tagline] : [])].join(\"\\n\");\n }\n\n const colored = BANNER_LINES.map((line, idx) => {\n const t = BANNER_LINES.length === 1 ? 0 : idx / (BANNER_LINES.length - 1);\n const [r, g, b] = gradientAt(t);\n return chalk.rgb(r, g, b).bold(line);\n });\n\n if (opts?.tagline) {\n colored.push(\"\");\n colored.push(chalk.dim(opts.tagline));\n }\n\n return colored.join(\"\\n\");\n}\n\n// Tiện ích: in banner ra stdout với newline phía trước/sau cho thoáng.\nexport function printAvatarBanner(opts?: { tagline?: string }): void {\n process.stdout.write(`\\n${renderAvatarBanner(opts)}\\n\\n`);\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"],"mappings":"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","names":["chalk","chalk"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nalvietnam/avatar-cli",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "AI harness CLI for NAL Vietnam engineering",
5
5
  "type": "module",
6
6
  "bin": {