@nalvietnam/avatar-cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/lib/terminal-logger.ts","../src/lib/not-implemented-stub.ts","../src/commands/commit.ts","../src/commands/doctor.ts","../src/lib/filesystem-helpers.ts","../src/lib/git-operations.ts","../src/lib/project-tree-scaffolder.ts","../src/lib/template-bundle-loader.ts","../src/lib/mustache-template-engine.ts","../src/lib/user-config-store.ts","../src/types/config-schema.ts","../src/commands/init.ts","../src/lib/audit-log-appender.ts","../src/lib/team-pack-submodule-manager.ts","../src/commands/login.ts","../src/lib/google-oauth-device-flow.ts","../src/commands/mcp-run.ts","../src/commands/restore.ts","../src/commands/review.ts","../src/commands/scan.ts","../src/commands/secrets.ts","../src/commands/status.ts","../src/lib/pack-backup-manager.ts","../src/commands/sync.ts","../src/commands/tools.ts"],"sourcesContent":["// Bootstrap: load commander, register all 13 subcommands, parse argv.\n// Each command lives in src/commands/<name>.ts as a function that accepts the\n// commander Command instance and registers itself.\nimport { Command } from \"commander\";\nimport { registerCommitCommand } from \"./commands/commit.js\";\nimport { registerDoctorCommand } from \"./commands/doctor.js\";\nimport { registerInitCommand } from \"./commands/init.js\";\nimport { registerLoginCommand } from \"./commands/login.js\";\nimport { registerMcpRunCommand } from \"./commands/mcp-run.js\";\nimport { registerRestoreCommand } from \"./commands/restore.js\";\nimport { registerReviewCommand } from \"./commands/review.js\";\nimport { registerScanCommand } from \"./commands/scan.js\";\nimport { registerSecretsCommand } from \"./commands/secrets.js\";\nimport { registerStatusCommand } from \"./commands/status.js\";\nimport { registerSyncCommand } from \"./commands/sync.js\";\nimport { registerToolsCommand } from \"./commands/tools.js\";\n\nconst CLI_VERSION = \"1.0.0\";\n\nconst program = new Command();\n\nprogram\n .name(\"avatar\")\n .description(\"AI harness CLI for NAL Vietnam engineering\")\n .version(CLI_VERSION, \"-v, --version\", \"Hiển thị phiên bản Avatar CLI\");\n\n// Register all commands. Order matches Chapter 09 spec in implementation doc.\nregisterLoginCommand(program);\nregisterInitCommand(program);\nregisterSyncCommand(program);\nregisterScanCommand(program);\nregisterReviewCommand(program);\nregisterStatusCommand(program);\nregisterDoctorCommand(program);\nregisterRestoreCommand(program);\nregisterCommitCommand(program);\nregisterToolsCommand(program);\nregisterSecretsCommand(program);\nregisterMcpRunCommand(program);\n\nprogram.parseAsync(process.argv).catch((err: unknown) => {\n // Top-level error sink. Individual commands should handle their own errors;\n // anything reaching here is unexpected.\n const msg = err instanceof Error ? err.message : String(err);\n process.stderr.write(`✗ Lỗi không xử lý được: ${msg}\\n`);\n process.exit(1);\n});\n","// Terminal logging helpers — chalk for color, ora for spinners.\n// All Avatar CLI output should funnel through here for consistent UX.\nimport chalk from \"chalk\";\nimport ora, { type Ora } from \"ora\";\n\ntype LogFn = (message: string) => void;\n\nexport const log: {\n info: LogFn;\n success: LogFn;\n warn: LogFn;\n error: LogFn;\n dim: LogFn;\n plain: LogFn;\n} = {\n info: (m) => process.stdout.write(`${chalk.blue(\"ℹ\")} ${m}\\n`),\n success: (m) => process.stdout.write(`${chalk.green(\"✓\")} ${m}\\n`),\n warn: (m) => process.stdout.write(`${chalk.yellow(\"⚠\")} ${m}\\n`),\n error: (m) => process.stderr.write(`${chalk.red(\"✗\")} ${m}\\n`),\n dim: (m) => process.stdout.write(`${chalk.dim(m)}\\n`),\n plain: (m) => process.stdout.write(`${m}\\n`),\n};\n\n// Spinner wrapper — handles non-TTY by degrading to plain output.\nexport function spinner(text: string): Ora {\n return ora({\n text,\n spinner: \"dots\",\n isEnabled: process.stdout.isTTY ?? false,\n }).start();\n}\n\n// Chalk re-export so commands don't all import chalk separately.\nexport { chalk };\n","// Shared stub action for commands wired into commander but not yet implemented.\n// Prints a consistent message + exit code so users know it's coming.\nimport { chalk } from \"./terminal-logger.js\";\n\nexport function notImplementedYet(commandName: string, milestone?: string): () => void {\n return () => {\n process.stdout.write(\n `${chalk.yellow(\"⏳\")} ${chalk.bold(`avatar ${commandName}`)} — chưa implement ở milestone hiện tại.\\n`,\n );\n if (milestone) {\n process.stdout.write(` Dự kiến: ${chalk.cyan(milestone)}\\n`);\n }\n process.stdout.write(\" Spec đã có trong avatar-cli-implementation_4.html.\\n\");\n process.exit(0);\n };\n}\n","// `avatar commit [--src|--avatar|--both] [-m <msg>] [--push]` — Command 09.\n// Only valid in client/library mode. Splits commits between the client repo\n// (src/) and the Avatar workspace.\nimport type { Command } from \"commander\";\nimport { notImplementedYet } from \"../lib/not-implemented-stub.js\";\n\nexport function registerCommitCommand(program: Command): void {\n program\n .command(\"commit\")\n .description(\"Commit code khách (src/) hoặc Avatar state riêng — chỉ client mode (M07)\")\n .option(\"--src\", \"Commit src/ → client remote\")\n .option(\"--avatar\", \"Commit Avatar state → workspace remote\")\n .option(\"--both\", \"Commit cả hai (src trước, avatar sau)\")\n .option(\"-m, --message <msg>\", \"Commit message\")\n .option(\"--push\", \"Tự động push sau khi commit\")\n .action(notImplementedYet(\"commit\", \"Milestone 07\"));\n}\n","import { spawnSync } from \"node:child_process\";\n// `avatar doctor [--fix]` — Command 07 spec.\n// Run a series of health checks and surface ✓/✗ for each. --fix attempts\n// auto-fixes for the ones safe to fix automatically.\nimport { promises as fs } from \"node:fs\";\nimport { join } from \"node:path\";\nimport boxen from \"boxen\";\nimport type { Command } from \"commander\";\nimport { pathExists } from \"../lib/filesystem-helpers.js\";\nimport { isGitRepo } from \"../lib/git-operations.js\";\nimport { installGitHook } from \"../lib/project-tree-scaffolder.js\";\nimport { chalk, log } from \"../lib/terminal-logger.js\";\nimport { isTokenExpired, readUserConfig } from \"../lib/user-config-store.js\";\n\ninterface CheckResult {\n name: string;\n status: \"ok\" | \"warn\" | \"fail\";\n detail: string;\n fixable: boolean;\n fix?: () => Promise<void>;\n}\n\nexport function registerDoctorCommand(program: Command): void {\n program\n .command(\"doctor\")\n .description(\"Chẩn đoán cài đặt Avatar: hooks, MCP, login, submodule, ...\")\n .option(\"--fix\", \"Tự động fix các issue có thể fix tự động\")\n .action(async (opts: { fix?: boolean }) => {\n try {\n const checks = await runChecks(process.cwd());\n renderChecks(checks);\n if (opts.fix) await applyFixes(checks);\n } catch (err) {\n log.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n });\n}\n\nasync function runChecks(cwd: string): Promise<CheckResult[]> {\n const checks: CheckResult[] = [];\n\n // 1. Node.js version >= 18.17\n const nodeVer = process.versions.node;\n const [major, minor] = nodeVer.split(\".\").map((n) => Number.parseInt(n, 10));\n const nodeOk = (major ?? 0) > 18 || ((major ?? 0) === 18 && (minor ?? 0) >= 17);\n checks.push({\n name: \"Node.js version\",\n status: nodeOk ? \"ok\" : \"fail\",\n detail: `v${nodeVer}${nodeOk ? \"\" : \" (cần >= 18.17)\"}`,\n fixable: false,\n });\n\n // 2. Login.\n const config = await readUserConfig();\n if (!config) {\n checks.push({\n name: \"Login status\",\n status: \"fail\",\n detail: \"Chưa đăng nhập — chạy 'avatar login'\",\n fixable: false,\n });\n } else if (isTokenExpired(config)) {\n checks.push({\n name: \"Login status\",\n status: \"warn\",\n detail: `Token hết hạn (${config.email}) — chạy 'avatar login'`,\n fixable: false,\n });\n } else {\n checks.push({\n name: \"Login status\",\n status: \"ok\",\n detail: `Logged in: ${config.email}`,\n fixable: false,\n });\n }\n\n // 3. Git repo.\n const gitRepo = await isGitRepo(cwd);\n checks.push({\n name: \"Git repository\",\n status: gitRepo ? \"ok\" : \"warn\",\n detail: gitRepo ? cwd : \"Không phải git repo (cần cho 'avatar init')\",\n fixable: false,\n });\n\n // 4. .claude/pack/ submodule.\n const packPath = join(cwd, \".claude\", \"pack\");\n const hasPack = await pathExists(packPath);\n checks.push({\n name: \"team-ai-pack submodule\",\n status: hasPack ? \"ok\" : \"warn\",\n detail: hasPack ? packPath : \"Avatar chưa init — chạy 'avatar init'\",\n fixable: false,\n });\n\n // 5. CLAUDE.md present.\n const claudeMdPath = join(cwd, \"CLAUDE.md\");\n const hasClaudeMd = await pathExists(claudeMdPath);\n checks.push({\n name: \"CLAUDE.md\",\n status: hasClaudeMd ? \"ok\" : \"warn\",\n detail: hasClaudeMd ? \"tồn tại ở project root\" : \"thiếu — chạy 'avatar init'\",\n fixable: false,\n });\n\n // 6. post-merge hook.\n const hookPath = join(cwd, \".git\", \"hooks\", \"post-merge\");\n const hasHook = await pathExists(hookPath);\n if (gitRepo && hasPack) {\n checks.push({\n name: \"Git hook post-merge\",\n status: hasHook ? \"ok\" : \"fail\",\n detail: hasHook ? \"installed\" : \"missing — fixable\",\n fixable: !hasHook,\n fix: hasHook\n ? undefined\n : async () => {\n await installGitHook(join(cwd, \".git\"), \"post-merge\");\n },\n });\n }\n\n // 7. .gitignore has Avatar entries.\n const gitignorePath = join(cwd, \".gitignore\");\n if (gitRepo) {\n let gitignoreOk = false;\n if (await pathExists(gitignorePath)) {\n const content = await fs.readFile(gitignorePath, \"utf8\");\n gitignoreOk = content.includes(\".claude/_pending/\");\n }\n checks.push({\n name: \".gitignore Avatar entries\",\n status: gitignoreOk ? \"ok\" : hasPack ? \"fail\" : \"warn\",\n detail: gitignoreOk ? \"có .claude/_pending/, .claude/_backup/\" : \"thiếu entries\",\n fixable: false,\n });\n }\n\n // 8. Claude Code CLI installed (best-effort `which claude`).\n const which = spawnSync(\"which\", [\"claude\"]);\n const hasClaudeCli = which.status === 0;\n checks.push({\n name: \"Claude Code CLI\",\n status: hasClaudeCli ? \"ok\" : \"warn\",\n detail: hasClaudeCli ? which.stdout.toString().trim() : \"không tìm thấy 'claude' trên PATH\",\n fixable: false,\n });\n\n return checks;\n}\n\nfunction renderChecks(checks: CheckResult[]): void {\n const lines = [chalk.bold(\"Avatar Doctor\"), \"─\".repeat(48)];\n let passed = 0;\n let issues = 0;\n let fixable = 0;\n for (const c of checks) {\n const icon =\n c.status === \"ok\"\n ? chalk.green(\"✓\")\n : c.status === \"warn\"\n ? chalk.yellow(\"⚠\")\n : chalk.red(\"✗\");\n lines.push(`${icon} ${c.name.padEnd(28)} ${chalk.dim(c.detail)}`);\n if (c.status === \"ok\") passed += 1;\n else {\n issues += 1;\n if (c.fixable) fixable += 1;\n }\n }\n lines.push(\"─\".repeat(48));\n lines.push(\n `${passed} checks passed, ${issues} issue${issues === 1 ? \"\" : \"s\"}${fixable > 0 ? ` (${fixable} fixable — chạy 'avatar doctor --fix')` : \"\"}`,\n );\n process.stdout.write(`${boxen(lines.join(\"\\n\"), { padding: 1, borderStyle: \"round\" })}\\n`);\n}\n\nasync function applyFixes(checks: CheckResult[]): Promise<void> {\n let count = 0;\n for (const c of checks) {\n if (c.fixable && c.fix) {\n try {\n await c.fix();\n log.success(`Fixed: ${c.name}`);\n count += 1;\n } catch (err) {\n log.error(`Failed to fix ${c.name}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n }\n if (count === 0) log.dim(\"Không có gì để fix tự động.\");\n}\n","// Thin promise-based wrappers around node:fs/promises with the patterns Avatar\n// uses most: ensure-dir, read-text, write-text-atomic, copy-dir-recursive.\n// Atomic write writes to a temp file then renames to avoid corruption if Avatar\n// crashes mid-write.\nimport { constants, promises as fs } from \"node:fs\";\nimport { dirname, join, relative } from \"node:path\";\n\nexport async function pathExists(path: string): Promise<boolean> {\n try {\n await fs.access(path, constants.F_OK);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function ensureDir(path: string): Promise<void> {\n await fs.mkdir(path, { recursive: true });\n}\n\nexport async function readText(path: string): Promise<string> {\n return await fs.readFile(path, \"utf8\");\n}\n\nexport async function readJson<T>(path: string): Promise<T> {\n return JSON.parse(await readText(path)) as T;\n}\n\n// Atomic write: write to temp file then rename. Prevents corruption if process\n// is killed between open() and close(). chmod is applied AFTER rename.\nexport async function writeTextAtomic(path: string, content: string, mode?: number): Promise<void> {\n await ensureDir(dirname(path));\n const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;\n await fs.writeFile(tmp, content, \"utf8\");\n if (mode !== undefined) {\n await fs.chmod(tmp, mode);\n }\n await fs.rename(tmp, path);\n}\n\nexport async function writeJsonAtomic(path: string, data: unknown, mode?: number): Promise<void> {\n await writeTextAtomic(path, `${JSON.stringify(data, null, 2)}\\n`, mode);\n}\n\n// Recursive copy. excludeNames filters by basename at any depth (e.g. \".git\").\nexport async function copyDirRecursive(\n source: string,\n destination: string,\n excludeNames: string[] = [],\n): Promise<void> {\n await ensureDir(destination);\n const entries = await fs.readdir(source, { withFileTypes: true });\n for (const entry of entries) {\n if (excludeNames.includes(entry.name)) continue;\n const src = join(source, entry.name);\n const dst = join(destination, entry.name);\n if (entry.isDirectory()) {\n await copyDirRecursive(src, dst, excludeNames);\n } else if (entry.isSymbolicLink()) {\n const target = await fs.readlink(src);\n await fs.symlink(target, dst);\n } else {\n await fs.copyFile(src, dst);\n }\n }\n}\n\nexport async function removeRecursive(path: string): Promise<void> {\n await fs.rm(path, { recursive: true, force: true });\n}\n\nexport function relativeFromCwd(path: string): string {\n return relative(process.cwd(), path);\n}\n","import { join } from \"node:path\";\n// Wrapper around simple-git for the operations Avatar uses repeatedly:\n// repo detection, submodule add/update, status, log, tag listing.\n// Centralised so error formatting and cwd handling stays consistent.\nimport { type SimpleGit, simpleGit } from \"simple-git\";\nimport { pathExists } from \"./filesystem-helpers.js\";\n\nexport function git(cwd: string = process.cwd()): SimpleGit {\n return simpleGit({ baseDir: cwd, binary: \"git\" });\n}\n\nexport async function isGitRepo(cwd: string = process.cwd()): Promise<boolean> {\n return await pathExists(join(cwd, \".git\"));\n}\n\nexport async function currentBranch(cwd: string = process.cwd()): Promise<string> {\n const result = await git(cwd).revparse([\"--abbrev-ref\", \"HEAD\"]);\n return result.trim();\n}\n\nexport async function addSubmodule(\n repoUrl: string,\n destPath: string,\n cwd: string = process.cwd(),\n): Promise<void> {\n await git(cwd).subModule([\"add\", repoUrl, destPath]);\n}\n\n// Checkout a tag inside a submodule. Used after `addSubmodule` to pin to a\n// specific team-ai-pack release.\nexport async function checkoutTagInSubmodule(\n submodulePath: string,\n tag: string,\n cwd: string = process.cwd(),\n): Promise<void> {\n const submoduleCwd = join(cwd, submodulePath);\n await git(submoduleCwd).fetch([\"--tags\"]);\n await git(submoduleCwd).checkout(tag);\n}\n\nexport async function listTags(cwd: string = process.cwd()): Promise<string[]> {\n const result = await git(cwd).tags();\n return result.all;\n}\n\nexport async function latestTag(cwd: string = process.cwd()): Promise<string | null> {\n const tags = await listTags(cwd);\n return tags.length > 0 ? (tags[tags.length - 1] ?? null) : null;\n}\n\nexport async function currentCommitSha(cwd: string = process.cwd()): Promise<string> {\n const result = await git(cwd).revparse([\"HEAD\"]);\n return result.trim();\n}\n\nexport async function workingTreeIsDirty(cwd: string = process.cwd()): Promise<boolean> {\n const status = await git(cwd).status();\n return !status.isClean();\n}\n","import { promises as fs } from \"node:fs\";\n// Scaffold the .claude/ directory tree inside a project after submodule add.\n// Used by `avatar init` for all three modes (internal/client/library).\n//\n// Spec source: Chapter 02 (folder map) + Chapter 09 Command 02 BEHAVIOR.\nimport { join } from \"node:path\";\nimport type { InitMode } from \"../types/config-schema.js\";\nimport { ensureDir, pathExists, writeTextAtomic } from \"./filesystem-helpers.js\";\nimport { type TemplateName, loadHook, renderTemplateByName } from \"./template-bundle-loader.js\";\n\n// Subdirectories Avatar creates inside .claude/ on init. `pack/` is added\n// separately by the submodule manager — listed here for reference only.\nconst CLAUDE_SUBDIRS = [\"project\", \"state\", \"_pending\", \"_backup\"] as const;\n\nconst PROJECT_KNOWLEDGE_TEMPLATES: TemplateName[] = [\n \"project/tech-stack.md\",\n \"project/conventions.md\",\n \"project/architecture.md\",\n \"project/domain.md\",\n \"project/gotchas.md\",\n];\n\nexport interface ScaffoldVariables {\n projectName: string;\n projectDescription: string;\n teamOwner: string;\n avatarVersion: string;\n packVersion: string;\n lastScan: string;\n mode: InitMode;\n}\n\n// Create .claude/{project,state,_pending,_backup}/ and a .gitkeep in each\n// so they survive a fresh checkout.\nexport async function createClaudeDirTree(projectRoot: string): Promise<void> {\n const claudeRoot = join(projectRoot, \".claude\");\n await ensureDir(claudeRoot);\n for (const sub of CLAUDE_SUBDIRS) {\n const dir = join(claudeRoot, sub);\n await ensureDir(dir);\n await writeTextAtomic(join(dir, \".gitkeep\"), \"\");\n }\n}\n\n// Render and write all 5 project knowledge files from templates.\n// All start with placeholder content — scanners fill them in later.\nexport async function writeProjectKnowledgeFiles(\n projectRoot: string,\n vars: ScaffoldVariables,\n): Promise<void> {\n const baseVars = {\n ...vars,\n primaryLanguage: \"(chưa scan)\",\n frameworks: \"(chưa scan)\",\n databases: \"(chưa scan)\",\n testStack: \"(chưa scan)\",\n buildStack: \"(chưa scan)\",\n toolVersions: \"(chưa scan)\",\n codeStyle: \"(chưa scan)\",\n namingConvention: \"(chưa scan)\",\n folderStructure: \"(chưa scan)\",\n commitConvention: \"(chưa scan)\",\n linterConfig: \"(chưa scan)\",\n architectureOverview: \"(chưa scan)\",\n moduleLayout: \"(chưa scan)\",\n dataFlow: \"(chưa scan)\",\n externalIntegrations: \"(chưa scan)\",\n deploymentTopology: \"(chưa scan)\",\n domainDescription: \"(chưa scan)\",\n coreEntities: \"(chưa scan)\",\n primaryUseCases: \"(chưa scan)\",\n domainGlossary: \"(chưa scan)\",\n };\n for (const tpl of PROJECT_KNOWLEDGE_TEMPLATES) {\n const content = await renderTemplateByName(tpl, baseVars);\n const relative = tpl.replace(/^project\\//, \"\");\n const outPath = join(projectRoot, \".claude\", \"project\", relative);\n await writeTextAtomic(outPath, content);\n }\n}\n\n// Write root CLAUDE.md from template — this is the entry point Claude Code reads.\nexport async function writeRootClaudeMd(\n projectRoot: string,\n vars: ScaffoldVariables,\n): Promise<void> {\n const content = await renderTemplateByName(\"CLAUDE.md\", vars);\n await writeTextAtomic(join(projectRoot, \"CLAUDE.md\"), content);\n}\n\n// Write .claude/settings.json from template.\nexport async function writeProjectSettings(\n projectRoot: string,\n vars: ScaffoldVariables,\n): Promise<void> {\n const content = await renderTemplateByName(\"settings.json\", vars);\n await writeTextAtomic(join(projectRoot, \".claude\", \"settings.json\"), content);\n}\n\n// Append Avatar entries to project .gitignore (or create if missing).\n// Idempotent — skips if our marker line already present.\nexport async function appendGitignoreEntries(projectRoot: string): Promise<void> {\n const path = join(projectRoot, \".gitignore\");\n const tpl = await renderTemplateByName(\"gitignore\", {});\n const marker = \"# Avatar — git-ignored entries injected on `avatar init`\";\n\n let existing = \"\";\n if (await pathExists(path)) {\n existing = await fs.readFile(path, \"utf8\");\n if (existing.includes(marker)) return;\n }\n\n const separator = existing.endsWith(\"\\n\") || existing.length === 0 ? \"\" : \"\\n\";\n await writeTextAtomic(path, `${existing}${separator}\\n${tpl}`);\n}\n\n// Install git hooks into <gitDir>/hooks/. `gitDir` lets caller point at\n// src/.git/hooks (client mode pre-push) or workspace .git/hooks (post-merge).\nexport async function installGitHook(\n gitDir: string,\n hookName: \"post-merge\" | \"pre-push\",\n): Promise<void> {\n const content = await loadHook(hookName);\n const hooksDir = join(gitDir, \"hooks\");\n await ensureDir(hooksDir);\n const dest = join(hooksDir, hookName);\n await writeTextAtomic(dest, content, 0o755);\n}\n","import { dirname, join } from \"node:path\";\n// Resolve template files bundled inside the Avatar CLI package. tsup bundles\n// src/ into dist/index.js but does NOT bundle template files — they ship as\n// loose files alongside dist/ in the npm tarball.\n//\n// At build/dev time templates live in <repo>/src/templates and <repo>/src/hooks.\n// At install time they live in <pkg>/src/templates and <pkg>/src/hooks (because\n// we also `files: [\"bin\", \"dist\", \"README.md\"]` — TODO: extend `files` to ship\n// templates with the package; for now resolve relative to import.meta.url).\nimport { fileURLToPath } from \"node:url\";\nimport { readText } from \"./filesystem-helpers.js\";\nimport { renderTemplate } from \"./mustache-template-engine.js\";\n\n// dist/index.js is the runtime location. Templates ship as siblings of dist/\n// under src/templates/ and src/hooks/ — see package.json `files` field.\nconst HERE = dirname(fileURLToPath(import.meta.url));\n\n// At dev time (`tsup --watch`) HERE points into dist/. At build time same.\n// Templates live at ../src/templates relative to the bundled file.\nconst TEMPLATES_ROOT = join(HERE, \"..\", \"src\", \"templates\");\nconst HOOKS_ROOT = join(HERE, \"..\", \"src\", \"hooks\");\n\nexport type TemplateName =\n | \"CLAUDE.md\"\n | \"settings.json\"\n | \"gitignore\"\n | \"project/tech-stack.md\"\n | \"project/conventions.md\"\n | \"project/architecture.md\"\n | \"project/domain.md\"\n | \"project/gotchas.md\";\n\nexport type HookName = \"post-merge\" | \"pre-push\";\n\nexport async function loadTemplate(name: TemplateName): Promise<string> {\n return await readText(join(TEMPLATES_ROOT, `${name}.tpl`));\n}\n\nexport async function renderTemplateByName(\n name: TemplateName,\n variables: Record<string, string | number | undefined>,\n): Promise<string> {\n const source = await loadTemplate(name);\n return renderTemplate(source, variables);\n}\n\nexport async function loadHook(name: HookName): Promise<string> {\n return await readText(join(HOOKS_ROOT, `${name}.sh.tpl`));\n}\n","// Minimal mustache-style {{variable}} replacement engine. Avoids pulling in\n// full mustache dependency — Avatar templates only need flat variable\n// substitution, no loops or conditionals.\n//\n// Unknown variables left as-is, NOT replaced with empty string. This makes\n// missing variables visible in output for easier debugging.\n\nconst TEMPLATE_PATTERN = /\\{\\{\\s*([a-zA-Z_][a-zA-Z0-9_]*)\\s*\\}\\}/g;\n\nexport function renderTemplate(\n source: string,\n variables: Record<string, string | number | undefined>,\n): string {\n return source.replace(TEMPLATE_PATTERN, (match, key: string) => {\n const value = variables[key];\n if (value === undefined) return match;\n return String(value);\n });\n}\n\n// Extract the variable names referenced by a template — useful for\n// scaffolding tests and validating that callers pass every required key.\nexport function extractVariables(source: string): string[] {\n const found = new Set<string>();\n for (const match of source.matchAll(TEMPLATE_PATTERN)) {\n if (match[1]) found.add(match[1]);\n }\n return Array.from(found).sort();\n}\n","// Read/write ~/.avatar/config.json (OAuth credentials) and ~/.avatar/state.json\n// (non-credential user state). Validates with Zod, enforces chmod 600 on config.\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport {\n type UserConfig,\n type UserState,\n userConfigSchema,\n userStateSchema,\n} from \"../types/config-schema.js\";\nimport { ensureDir, pathExists, readJson, writeJsonAtomic } from \"./filesystem-helpers.js\";\n\nexport const AVATAR_HOME = join(homedir(), \".avatar\");\nexport const USER_CONFIG_PATH = join(AVATAR_HOME, \"config.json\");\nexport const USER_STATE_PATH = join(AVATAR_HOME, \"state.json\");\nexport const AUDIT_LOG_PATH = join(AVATAR_HOME, \"audit.log\");\nexport const BACKUPS_DIR = join(AVATAR_HOME, \"backups\");\n\n// chmod 600 — owner-only read/write. Matches spec for credential files.\nconst SECRET_FILE_MODE = 0o600;\n\nexport async function ensureAvatarHome(): Promise<void> {\n await ensureDir(AVATAR_HOME);\n}\n\nexport async function readUserConfig(): Promise<UserConfig | null> {\n if (!(await pathExists(USER_CONFIG_PATH))) return null;\n const raw = await readJson<unknown>(USER_CONFIG_PATH);\n const parsed = userConfigSchema.safeParse(raw);\n if (!parsed.success) return null;\n return parsed.data;\n}\n\nexport async function writeUserConfig(config: UserConfig): Promise<void> {\n await ensureAvatarHome();\n await writeJsonAtomic(USER_CONFIG_PATH, config, SECRET_FILE_MODE);\n}\n\nexport async function clearUserConfig(): Promise<void> {\n if (await pathExists(USER_CONFIG_PATH)) {\n const { promises: fs } = await import(\"node:fs\");\n await fs.unlink(USER_CONFIG_PATH);\n }\n}\n\nexport async function readUserState(): Promise<UserState> {\n if (!(await pathExists(USER_STATE_PATH))) {\n return userStateSchema.parse({});\n }\n const raw = await readJson<unknown>(USER_STATE_PATH);\n const parsed = userStateSchema.safeParse(raw);\n if (!parsed.success) return userStateSchema.parse({});\n return parsed.data;\n}\n\nexport async function writeUserState(state: UserState): Promise<void> {\n await ensureAvatarHome();\n await writeJsonAtomic(USER_STATE_PATH, state);\n}\n\n// Token is considered expired if it would expire in the next 60 seconds.\n// 60s buffer accounts for slow network round-trip on the upcoming request.\nexport function isTokenExpired(config: UserConfig): boolean {\n const expiresAt = Date.parse(config.expires_at);\n return Number.isNaN(expiresAt) || expiresAt - Date.now() < 60_000;\n}\n","// Zod schemas + inferred TypeScript types for all on-disk Avatar config files.\n// Centralised so commands import from one place and validation matches storage.\nimport { z } from \"zod\";\n\n// ~/.avatar/config.json — OAuth credentials after `avatar login`.\n// chmod 600 enforced at write site.\nexport const userConfigSchema = z.object({\n email: z.string().email(),\n name: z.string(),\n access_token: z.string().min(1),\n refresh_token: z.string().min(1),\n expires_at: z.string().datetime(),\n id_token: z.string().min(1),\n});\nexport type UserConfig = z.infer<typeof userConfigSchema>;\n\n// ~/.avatar/state.json — non-credential user state (tool install records,\n// allowed-paths chosen for filesystem MCP, etc).\nexport const userStateSchema = z.object({\n installed_tools: z\n .record(\n z.string(),\n z.object({\n version: z.string().optional(),\n installed_at: z.string().datetime(),\n install_method: z.string(),\n }),\n )\n .default({}),\n tool_inputs: z.record(z.string(), z.unknown()).default({}),\n});\nexport type UserState = z.infer<typeof userStateSchema>;\n\n// .claude/settings.json — per-project Claude Code settings emitted by `avatar init`.\nexport const projectSettingsSchema = z.object({\n allowedTools: z.array(z.string()),\n hooks: z\n .object({\n PostToolUse: z.array(z.unknown()).optional(),\n })\n .partial()\n .optional(),\n env: z.record(z.string(), z.string()).default({}),\n});\nexport type ProjectSettings = z.infer<typeof projectSettingsSchema>;\n\n// Init mode — determines workspace topology (Chapter 02 + Chapter 07 spec).\nexport const initModeSchema = z.enum([\"internal\", \"client\", \"library\"]);\nexport type InitMode = z.infer<typeof initModeSchema>;\n","// `avatar init` — Command 02 spec.\n// Three modes: internal (Avatar files commit with code), client (Pattern A,\n// workspace + src/ submodule), library (same topology as client).\n//\n// This implementation focuses on the structural work: submodules, directory\n// tree, templates, git hooks. The scanner wizard step (Câu 3/3 — MCP tools)\n// is deferred — tools.ts is stubbed for this milestone.\n\nimport { join, resolve } from \"node:path\";\nimport { confirm, input, select } from \"@inquirer/prompts\";\nimport boxen from \"boxen\";\nimport type { Command } from \"commander\";\nimport { appendAuditEntry } from \"../lib/audit-log-appender.js\";\nimport { ensureDir, pathExists } from \"../lib/filesystem-helpers.js\";\nimport { git, isGitRepo } from \"../lib/git-operations.js\";\nimport {\n type ScaffoldVariables,\n appendGitignoreEntries,\n createClaudeDirTree,\n installGitHook,\n writeProjectKnowledgeFiles,\n writeProjectSettings,\n writeRootClaudeMd,\n} from \"../lib/project-tree-scaffolder.js\";\nimport { TEAM_PACK_REPO_URL, addTeamPackSubmodule } from \"../lib/team-pack-submodule-manager.js\";\nimport { chalk, log, spinner } from \"../lib/terminal-logger.js\";\nimport { isTokenExpired, readUserConfig } from \"../lib/user-config-store.js\";\nimport type { InitMode } from \"../types/config-schema.js\";\n\nconst AVATAR_CLI_VERSION = \"1.0.0\";\n\ninterface InitOptions {\n mode?: InitMode;\n skipScan?: boolean;\n packVersion?: string;\n clientRepo?: string;\n workspaceName?: string;\n workspaceParent?: string;\n}\n\nexport function registerInitCommand(program: Command): void {\n program\n .command(\"init\")\n .description(\"Khởi tạo Avatar trong dự án (3 mode: internal/client/library)\")\n .option(\"--mode <mode>\", \"internal | client | library\")\n .option(\"--skip-scan\", \"Bỏ qua bước project-scanner\")\n .option(\"--pack-version <tag>\", \"Pin team-ai-pack vào version cụ thể\")\n .option(\"--client-repo <url>\", \"URL git của client repo (mode=client)\")\n .option(\"--workspace-name <name>\", \"Tên workspace (mode=client)\")\n .option(\"--workspace-parent <path>\", \"Thư mục cha tạo workspace (mode=client)\")\n .action(async (opts: InitOptions) => {\n try {\n await runInit(opts);\n } catch (err) {\n log.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n });\n}\n\nasync function runInit(opts: InitOptions): Promise<void> {\n // Precondition: logged in.\n const userConfig = await readUserConfig();\n if (!userConfig || isTokenExpired(userConfig)) {\n log.error(\"Chưa đăng nhập hoặc token đã hết hạn. Chạy 'avatar login' trước.\");\n process.exit(1);\n }\n\n // Step 1: determine mode. If not provided, prompt.\n const mode = opts.mode ?? (await promptMode());\n\n // Step 2: route by mode.\n if (mode === \"internal\") {\n await runInitInternal(opts, userConfig.email);\n } else {\n await runInitClientOrLibrary(opts, mode, userConfig.email);\n }\n}\n\nasync function promptMode(): Promise<InitMode> {\n return (await select({\n message: \"Đây là loại dự án gì?\",\n choices: [\n {\n name: \"Nội bộ NAL (Avatar files commit cùng code)\",\n value: \"internal\" as const,\n },\n { name: \"Client (Pattern A — tách workspace)\", value: \"client\" as const },\n { name: \"Library/SDK public (tách workspace)\", value: \"library\" as const },\n ],\n })) as InitMode;\n}\n\n// ─── MODE: INTERNAL ─────────────────────────────────────────────────────────\nasync function runInitInternal(opts: InitOptions, ownerEmail: string): Promise<void> {\n const projectRoot = process.cwd();\n\n if (!(await isGitRepo(projectRoot))) {\n throw new Error(\"Mode internal cần dự án đã là git repo. Chạy 'git init' trước rồi thử lại.\");\n }\n if (await pathExists(join(projectRoot, \".claude\"))) {\n throw new Error(\n \".claude/ đã tồn tại. Avatar không override. Xóa thủ công hoặc dùng mode khác.\",\n );\n }\n\n const teamOwner = await promptTeamOwner(ownerEmail);\n\n const projectName = projectNameOf(projectRoot);\n const projectDescription = await input({\n message: \"Mô tả ngắn 1 dòng của dự án:\",\n default: `Avatar-managed project: ${projectName}`,\n });\n\n // Add team-ai-pack submodule.\n const sp = spinner(`Thêm submodule team-ai-pack từ ${TEAM_PACK_REPO_URL}...`);\n let pinnedTag: string | null = null;\n try {\n const result = await addTeamPackSubmodule(projectRoot, opts.packVersion);\n pinnedTag = result.pinnedTag;\n sp.succeed(`Đã pin team-ai-pack vào ${pinnedTag ?? \"HEAD\"}`);\n } catch (err) {\n sp.fail(\"Add submodule thất bại\");\n throw err;\n }\n\n // Scaffold .claude/ tree + templates.\n const vars = buildScaffoldVariables({\n projectName,\n projectDescription,\n teamOwner,\n packVersion: pinnedTag ?? \"HEAD\",\n mode: \"internal\",\n });\n await createClaudeDirTree(projectRoot);\n await writeProjectKnowledgeFiles(projectRoot, vars);\n await writeRootClaudeMd(projectRoot, vars);\n await writeProjectSettings(projectRoot, vars);\n await appendGitignoreEntries(projectRoot);\n\n // Install post-merge hook in project's .git/hooks/.\n await installGitHook(join(projectRoot, \".git\"), \"post-merge\");\n log.success(\"Cài git hook post-merge\");\n\n await appendAuditEntry(\"init\", `mode=internal,project=${projectName}`);\n await maybeCommit(projectRoot, \"internal\");\n printInitSuccessBox(projectRoot, \"internal\");\n}\n\n// ─── MODE: CLIENT / LIBRARY ─────────────────────────────────────────────────\nasync function runInitClientOrLibrary(\n opts: InitOptions,\n mode: \"client\" | \"library\",\n ownerEmail: string,\n): Promise<void> {\n // Collect required inputs (5 wizard questions per spec).\n const teamOwner = await promptTeamOwner(ownerEmail);\n const clientRepoUrl =\n opts.clientRepo ??\n (await input({\n message: \"URL git của client repo:\",\n validate: (v) => (v.length > 0 ? true : \"URL bắt buộc\"),\n }));\n const inferredName = inferWorkspaceName(clientRepoUrl);\n const workspaceName =\n opts.workspaceName ??\n (await input({\n message: \"Tên workspace:\",\n default: inferredName,\n }));\n const workspaceParent = resolve(opts.workspaceParent ?? \"..\");\n\n const workspacePath = join(workspaceParent, workspaceName);\n if (await pathExists(workspacePath)) {\n throw new Error(`Workspace path đã tồn tại: ${workspacePath}`);\n }\n\n // Create workspace + git init.\n await ensureDir(workspacePath);\n await git(workspacePath).init();\n\n // Add both submodules in parallel.\n const sp = spinner(\"Add submodule client repo + team-ai-pack...\");\n try {\n await git(workspacePath).subModule([\"add\", clientRepoUrl, \"src\"]);\n const result = await addTeamPackSubmodule(workspacePath, opts.packVersion);\n sp.succeed(`Workspace pin team-ai-pack vào ${result.pinnedTag ?? \"HEAD\"}`);\n\n // Scaffold workspace knowledge using projectName derived from workspace.\n const vars = buildScaffoldVariables({\n projectName: workspaceName,\n projectDescription: `Avatar ${mode} workspace for ${clientRepoUrl}`,\n teamOwner,\n packVersion: result.pinnedTag ?? \"HEAD\",\n mode,\n });\n\n await createClaudeDirTree(workspacePath);\n await writeProjectKnowledgeFiles(workspacePath, vars);\n await writeRootClaudeMd(workspacePath, vars);\n await writeProjectSettings(workspacePath, vars);\n await appendGitignoreEntries(workspacePath);\n\n // Extra dirs only client/library mode uses.\n await ensureDir(join(workspacePath, \"notes\"));\n await ensureDir(join(workspacePath, \"scripts\"));\n\n // Install post-merge in workspace, pre-push in src/ to block Avatar leaks.\n await installGitHook(join(workspacePath, \".git\"), \"post-merge\");\n await installGitHook(join(workspacePath, \"src\", \".git\"), \"pre-push\");\n log.success(\"Cài post-merge (workspace) + pre-push (src/)\");\n\n await appendAuditEntry(\"init\", `mode=${mode},workspace=${workspaceName}`);\n await maybeCommitWorkspace(workspacePath);\n printInitSuccessBox(workspacePath, mode);\n } catch (err) {\n sp.fail(\"Init workspace thất bại\");\n throw err;\n }\n}\n\n// ─── helpers ────────────────────────────────────────────────────────────────\n\nfunction projectNameOf(projectRoot: string): string {\n return projectRoot.split(\"/\").filter(Boolean).pop() ?? \"avatar-project\";\n}\n\nfunction inferWorkspaceName(repoUrl: string): string {\n // git@github.com:org/repo.git → repo\n const m = repoUrl.match(/[/:]([^/]+?)(\\.git)?$/);\n const base = m?.[1] ?? \"client\";\n return `avatar-${base}-workspace`;\n}\n\nasync function promptTeamOwner(currentUserEmail: string): Promise<string> {\n return await input({\n message: \"Team owner email:\",\n default: currentUserEmail,\n });\n}\n\nfunction buildScaffoldVariables(args: {\n projectName: string;\n projectDescription: string;\n teamOwner: string;\n packVersion: string;\n mode: InitMode;\n}): ScaffoldVariables {\n return {\n projectName: args.projectName,\n projectDescription: args.projectDescription,\n teamOwner: args.teamOwner,\n avatarVersion: AVATAR_CLI_VERSION,\n packVersion: args.packVersion,\n lastScan: new Date().toISOString(),\n mode: args.mode,\n };\n}\n\nasync function maybeCommit(projectRoot: string, mode: InitMode): Promise<void> {\n const wantCommit = await confirm({\n message: \"Commit ngay các file Avatar đã tạo?\",\n default: true,\n });\n if (!wantCommit) return;\n const g = git(projectRoot);\n await g.add([\".claude/\", \"CLAUDE.md\", \".gitignore\", \".gitmodules\"]);\n await g.commit(`chore: initialize Avatar in ${mode} mode`);\n log.success(\"Đã commit\");\n}\n\nasync function maybeCommitWorkspace(workspacePath: string): Promise<void> {\n const wantCommit = await confirm({\n message: \"Commit workspace ngay?\",\n default: true,\n });\n if (!wantCommit) return;\n const g = git(workspacePath);\n await g.add([\"CLAUDE.md\", \".claude/\", \".gitignore\", \".gitmodules\", \"notes/\", \"scripts/\"]);\n await g.commit(\"chore: initialize Avatar workspace for client\");\n log.success(\"Đã commit workspace\");\n}\n\nfunction printInitSuccessBox(rootPath: string, mode: InitMode): void {\n const lines: string[] = [];\n if (mode === \"internal\") {\n lines.push(`${chalk.green(\"✓\")} Avatar đã sẵn sàng trong dự án`);\n lines.push(\"\");\n lines.push(` ${chalk.cyan(\"claude\")} Mở Claude Code`);\n lines.push(` ${chalk.cyan(\"avatar status\")} Xem snapshot`);\n lines.push(` ${chalk.cyan(\"avatar sync\")} Pull team-ai-pack mới`);\n } else {\n lines.push(`${chalk.green(\"✓\")} Workspace sẵn sàng: ${rootPath}`);\n lines.push(\"\");\n lines.push(` ${chalk.cyan(`cd ${rootPath}`)}`);\n lines.push(` ${chalk.cyan(\"claude\")} Mở Claude Code ở workspace root`);\n lines.push(\"\");\n lines.push(` ${chalk.cyan(\"avatar commit --src\")} Commit code khách lên client remote`);\n lines.push(\n ` ${chalk.cyan(\"avatar commit --avatar\")} Commit Avatar state lên workspace remote`,\n );\n lines.push(` ${chalk.cyan(\"avatar sync\")} Sync team pack`);\n }\n process.stdout.write(`${boxen(lines.join(\"\\n\"), { padding: 1, borderStyle: \"round\" })}\\n`);\n}\n","// Append-only audit log at ~/.avatar/audit.log. Records sensitive actions\n// (secrets set/rm, tool install/remove) WITHOUT logging secret values.\n// Used by `avatar doctor` to surface recent activity.\nimport { promises as fs } from \"node:fs\";\nimport { AUDIT_LOG_PATH, ensureAvatarHome } from \"./user-config-store.js\";\n\nexport type AuditAction =\n | \"login\"\n | \"login_reset\"\n | \"logout\"\n | \"secret_set\"\n | \"secret_rm\"\n | \"tool_install\"\n | \"tool_remove\"\n | \"init\"\n | \"sync\"\n | \"restore\";\n\nexport interface AuditEntry {\n timestamp: string;\n action: AuditAction;\n detail?: string;\n}\n\nexport async function appendAuditEntry(action: AuditAction, detail?: string): Promise<void> {\n await ensureAvatarHome();\n const entry: AuditEntry = {\n timestamp: new Date().toISOString(),\n action,\n ...(detail ? { detail } : {}),\n };\n const line = `${JSON.stringify(entry)}\\n`;\n await fs.appendFile(AUDIT_LOG_PATH, line, \"utf8\");\n}\n","// Manage the team-ai-pack git submodule lifecycle: add, update, pin-to-tag,\n// changelog extraction. Used by `avatar init` and `avatar sync`.\nimport { join } from \"node:path\";\nimport {\n addSubmodule,\n checkoutTagInSubmodule,\n currentCommitSha,\n latestTag,\n} from \"./git-operations.js\";\n\nexport const TEAM_PACK_REPO_URL = \"https://github.com/LukeNALS/team-ai-pack.git\";\nexport const TEAM_PACK_RELATIVE_PATH = \".claude/pack\";\n\n// Add the team-ai-pack submodule into a fresh project and pin it to a tag.\n// If `tag` is omitted, checks out the latest tag in the freshly-cloned submodule.\nexport async function addTeamPackSubmodule(\n projectRoot: string,\n tag?: string,\n): Promise<{ pinnedTag: string | null }> {\n await addSubmodule(TEAM_PACK_REPO_URL, TEAM_PACK_RELATIVE_PATH, projectRoot);\n\n // Resolve which tag to pin to. If caller passed one, honour it; otherwise\n // ask the just-cloned submodule for its latest tag.\n let target = tag ?? null;\n if (!target) {\n target = await latestTag(join(projectRoot, TEAM_PACK_RELATIVE_PATH));\n }\n\n if (target) {\n await checkoutTagInSubmodule(TEAM_PACK_RELATIVE_PATH, target, projectRoot);\n }\n return { pinnedTag: target };\n}\n\n// Read the current pinned version of the pack submodule. Returns the tag name\n// if HEAD matches a tag, otherwise the short SHA.\nexport async function readPinnedPackVersion(projectRoot: string): Promise<string> {\n const submoduleRoot = join(projectRoot, TEAM_PACK_RELATIVE_PATH);\n const tag = await latestTag(submoduleRoot);\n if (tag) return tag;\n const sha = await currentCommitSha(submoduleRoot);\n return sha.slice(0, 7);\n}\n","import boxen from \"boxen\";\n// `avatar login [--reset]` — Command 01 spec.\n// Implements the user-facing flow: announce verification URL, open browser,\n// poll Google until token returned, validate domain, persist credentials.\nimport type { Command } from \"commander\";\nimport open from \"open\";\nimport { appendAuditEntry } from \"../lib/audit-log-appender.js\";\nimport {\n buildUserConfig,\n buildVerificationUrl,\n decodeIdToken,\n pollForToken,\n requestDeviceCode,\n revokeToken,\n verifyHostedDomain,\n} from \"../lib/google-oauth-device-flow.js\";\nimport { chalk, log, spinner } from \"../lib/terminal-logger.js\";\nimport {\n USER_CONFIG_PATH,\n clearUserConfig,\n isTokenExpired,\n readUserConfig,\n writeUserConfig,\n} from \"../lib/user-config-store.js\";\n\nexport function registerLoginCommand(program: Command): void {\n program\n .command(\"login\")\n .description(\"Đăng nhập Google SSO (workspace @nal.vn)\")\n .option(\"--reset\", \"Xóa credential cũ và đăng nhập lại\")\n .action(async (opts: { reset?: boolean }) => {\n try {\n await runLogin(opts);\n } catch (err) {\n log.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n });\n}\n\nasync function runLogin(opts: { reset?: boolean }): Promise<void> {\n // Step 1 of spec: short-circuit if already logged in and token is still good.\n if (opts.reset) {\n await clearUserConfig();\n await appendAuditEntry(\"login_reset\");\n } else {\n const existing = await readUserConfig();\n if (existing && !isTokenExpired(existing)) {\n log.success(`Đã đăng nhập: ${existing.email}`);\n return;\n }\n }\n\n // Step 2: request device + user code.\n const deviceSpinner = spinner(\"Đang yêu cầu device code từ Google...\");\n let deviceCode: Awaited<ReturnType<typeof requestDeviceCode>>;\n try {\n deviceCode = await requestDeviceCode();\n deviceSpinner.succeed(\"Nhận device code\");\n } catch (err) {\n deviceSpinner.fail(\"Không kết nối được Google\");\n throw err;\n }\n\n // Step 3: display instructions to user.\n const verificationUrl = buildVerificationUrl(deviceCode);\n const instructions = [\n `1. Truy cập: ${chalk.cyan(deviceCode.verification_url)}`,\n `2. Nhập code: ${chalk.bold.yellow(deviceCode.user_code)}`,\n \"\",\n `Hoặc Avatar tự mở browser, click ${chalk.green(\"Allow\")}...`,\n ].join(\"\\n\");\n process.stdout.write(`${boxen(instructions, { padding: 1, borderStyle: \"round\" })}\\n`);\n\n // Step 4: open browser. Failure here is non-fatal — user can copy URL manually.\n void open(verificationUrl).catch(() => {\n log.dim(\"(Không mở được browser tự động — copy URL ở trên)\");\n });\n\n // Step 5: poll token endpoint until success or expiry.\n const waitSpinner = spinner(\"Đang chờ xác nhận trong browser...\");\n const intervalMs = deviceCode.interval * 1000;\n const deadline = Date.now() + deviceCode.expires_in * 1000;\n\n let token = null;\n while (Date.now() < deadline) {\n await sleep(intervalMs);\n try {\n token = await pollForToken(deviceCode.device_code);\n if (token) break;\n } catch (err) {\n waitSpinner.fail(\"Xác thực thất bại\");\n throw err;\n }\n }\n if (!token) {\n waitSpinner.fail(\"Hết hạn chờ (5 phút). Chạy lại 'avatar login'.\");\n process.exit(1);\n }\n waitSpinner.succeed(\"Đã nhận token từ Google\");\n\n // Step 6: verify hosted domain. Revoke token if claim is wrong.\n const claims = decodeIdToken(token.id_token);\n try {\n verifyHostedDomain(claims);\n } catch (err) {\n await revokeToken(token.access_token);\n throw err;\n }\n\n // Step 7: persist credentials with chmod 600.\n const userConfig = buildUserConfig(token, claims);\n await writeUserConfig(userConfig);\n await appendAuditEntry(\"login\", userConfig.email);\n\n log.success(`Xác thực thành công: ${userConfig.email}`);\n log.success(`Verify hosted domain: ${claims.hd} ✓`);\n log.success(`Lưu credential vào ${USER_CONFIG_PATH} (chmod 600)`);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","// Google OAuth 2.0 Device Authorization Grant (RFC 8628) implementation.\n//\n// Why Device Flow: Avatar is a terminal CLI with no browser redirect URL.\n// The user logs in via google.com/device on a browser and the CLI polls\n// Google for the resulting token.\n//\n// Why the client secret is bundled in source: Device Flow does not treat the\n// secret as a security boundary — the human Allow click in the browser is.\n// Google's TV/Limited Input docs explicitly permit this.\n//\n// To rotate: Google Cloud Console → APIs & Services → Credentials → click the\n// OAuth client → Reset Secret. Replace GOOGLE_CLIENT_SECRET below.\n\nimport type { UserConfig } from \"../types/config-schema.js\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// OAuth client config (hardcoded — see file header for rationale).\n// To regenerate: Google Cloud Console → project \"avatar-cli\" → Clients.\n// Application type must be \"TV and Limited Input devices\".\n// ─────────────────────────────────────────────────────────────────────────────\nexport const GOOGLE_CLIENT_ID =\n \"1014766441755-i4jimivh5rd7vt8phuhmepmt45sovtph.apps.googleusercontent.com\";\nexport const GOOGLE_CLIENT_SECRET = \"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\";\n\n// Restrict to the NAL Workspace domain. Enforced at TWO layers:\n// 1. ?hd=nal.vn appended to the verification URL — Google filters the picker\n// 2. Verify id_token.hd === HOSTED_DOMAIN after token exchange (defense-in-depth)\nexport const HOSTED_DOMAIN = \"nal.vn\";\n\nexport const SCOPES = [\"openid\", \"email\", \"profile\"];\n\nconst DEVICE_CODE_URL = \"https://oauth2.googleapis.com/device/code\";\nconst TOKEN_URL = \"https://oauth2.googleapis.com/token\";\nconst REVOKE_URL = \"https://oauth2.googleapis.com/revoke\";\n\nexport interface DeviceCodeResponse {\n device_code: string;\n user_code: string;\n verification_url: string;\n expires_in: number;\n interval: number;\n}\n\nexport interface TokenResponse {\n access_token: string;\n refresh_token: string;\n id_token: string;\n expires_in: number;\n token_type: string;\n scope: string;\n}\n\nexport interface IdTokenClaims {\n email: string;\n email_verified: boolean;\n name?: string;\n hd?: string;\n exp: number;\n iss: string;\n aud: string;\n}\n\n// ── Step 2 of Command 01 spec: request device + user codes.\nexport async function requestDeviceCode(): Promise<DeviceCodeResponse> {\n const body = new URLSearchParams({\n client_id: GOOGLE_CLIENT_ID,\n scope: SCOPES.join(\" \"),\n });\n const res = await fetch(DEVICE_CODE_URL, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body,\n });\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`Device code request failed (${res.status}): ${text}`);\n }\n return (await res.json()) as DeviceCodeResponse;\n}\n\n// ── Step 5: poll the token endpoint until user authorises or expiry.\n// Returns the token response on success, null while still pending.\n// Throws on hard errors (access_denied, expired_token).\nexport async function pollForToken(deviceCode: string): Promise<TokenResponse | null> {\n const body = new URLSearchParams({\n client_id: GOOGLE_CLIENT_ID,\n client_secret: GOOGLE_CLIENT_SECRET,\n device_code: deviceCode,\n grant_type: \"urn:ietf:params:oauth:grant-type:device_code\",\n });\n const res = await fetch(TOKEN_URL, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body,\n });\n\n if (res.ok) {\n return (await res.json()) as TokenResponse;\n }\n\n // Google returns 4xx with a JSON {error} field for both pending and fatal states.\n let errorCode = \"\";\n try {\n const data = (await res.json()) as { error?: string };\n errorCode = data.error ?? \"\";\n } catch {\n errorCode = \"\";\n }\n\n if (errorCode === \"authorization_pending\" || errorCode === \"slow_down\") {\n return null;\n }\n if (errorCode === \"access_denied\") {\n throw new Error(\"User từ chối quyền truy cập\");\n }\n if (errorCode === \"expired_token\") {\n throw new Error(\"Hết hạn chờ (5 phút). Chạy lại 'avatar login'.\");\n }\n throw new Error(`OAuth token endpoint trả lỗi: ${errorCode || res.status}`);\n}\n\n// Decode JWT payload WITHOUT verifying signature. Safe here because we receive\n// the token directly from Google over HTTPS — no MITM surface.\nexport function decodeIdToken(idToken: string): IdTokenClaims {\n const parts = idToken.split(\".\");\n if (parts.length !== 3) {\n throw new Error(\"id_token format không hợp lệ\");\n }\n const payload = parts[1];\n if (!payload) throw new Error(\"id_token thiếu payload\");\n // Convert base64url to base64.\n const base64 = payload.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const json = Buffer.from(base64, \"base64\").toString(\"utf8\");\n return JSON.parse(json) as IdTokenClaims;\n}\n\n// ── Step 6: enforce hosted domain. Reject any non-@nal.vn account.\nexport function verifyHostedDomain(claims: IdTokenClaims): void {\n if (claims.hd !== HOSTED_DOMAIN) {\n throw new Error(\n `Email không thuộc workspace NAL (yêu cầu @${HOSTED_DOMAIN}). Nhận: ${claims.email}`,\n );\n }\n if (!claims.email_verified) {\n throw new Error(\"Email chưa được Google verify\");\n }\n}\n\n// Convert OAuth token response + decoded claims into the on-disk UserConfig shape.\nexport function buildUserConfig(token: TokenResponse, claims: IdTokenClaims): UserConfig {\n const expiresAt = new Date(Date.now() + token.expires_in * 1000).toISOString();\n return {\n email: claims.email,\n name: claims.name ?? claims.email,\n access_token: token.access_token,\n refresh_token: token.refresh_token,\n expires_at: expiresAt,\n id_token: token.id_token,\n };\n}\n\n// Refresh flow: exchange refresh_token for a new access_token. Used by other\n// commands when they detect an expired token (see isTokenExpired).\nexport async function refreshAccessToken(refreshToken: string): Promise<{\n access_token: string;\n expires_in: number;\n id_token?: string;\n}> {\n const body = new URLSearchParams({\n client_id: GOOGLE_CLIENT_ID,\n client_secret: GOOGLE_CLIENT_SECRET,\n refresh_token: refreshToken,\n grant_type: \"refresh_token\",\n });\n const res = await fetch(TOKEN_URL, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body,\n });\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`Refresh token failed (${res.status}): ${text}`);\n }\n return (await res.json()) as { access_token: string; expires_in: number; id_token?: string };\n}\n\n// Revoke a token (used when login is rejected post-hoc, e.g. wrong domain).\nexport async function revokeToken(token: string): Promise<void> {\n const body = new URLSearchParams({ token });\n await fetch(REVOKE_URL, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body,\n }).catch(() => {\n // Best-effort revoke — don't fail the caller if revoke itself errors.\n });\n}\n\n// Build the verification URL with hd hint so Google pre-filters the account picker.\nexport function buildVerificationUrl(response: DeviceCodeResponse): string {\n const url = new URL(response.verification_url);\n url.searchParams.set(\"user_code\", response.user_code);\n url.searchParams.set(\"hd\", HOSTED_DOMAIN);\n return url.toString();\n}\n","// `avatar mcp-run <tool-id>` — hidden command from Chapter 13 roadmap.\n// Wrapper used by ~/.claude.json entries: Avatar injects secrets from keychain\n// then spawns the underlying MCP process with stdio piped to Claude Code.\nimport type { Command } from \"commander\";\nimport { notImplementedYet } from \"../lib/not-implemented-stub.js\";\n\nexport function registerMcpRunCommand(program: Command): void {\n program\n .command(\"mcp-run <tool-id>\", { hidden: true })\n .description(\"[internal] Spawn MCP với secrets injected (M09)\")\n .action(notImplementedYet(\"mcp-run\", \"Milestone 09\"));\n}\n","// `avatar restore [--backup <name>] [--list]` — Command 08 spec.\n// Restore .claude/pack/ from a previous backup snapshot.\nimport type { Command } from \"commander\";\nimport { notImplementedYet } from \"../lib/not-implemented-stub.js\";\n\nexport function registerRestoreCommand(program: Command): void {\n program\n .command(\"restore\")\n .description(\"Khôi phục .claude/pack/ từ backup (M08)\")\n .option(\"--backup <name>\", \"Tên backup folder trong .claude/_backup/\")\n .option(\"--list\", \"Liệt kê các backup hiện có\")\n .action(notImplementedYet(\"restore\", \"Milestone 08\"));\n}\n","// `avatar review [--accept-all|--reject-all]` — Command 05 spec.\n// Interactive review of .claude/_pending/*.diff.md proposals.\nimport type { Command } from \"commander\";\nimport { notImplementedYet } from \"../lib/not-implemented-stub.js\";\n\nexport function registerReviewCommand(program: Command): void {\n program\n .command(\"review\")\n .description(\"Review pending proposals từ avatar scan (M08)\")\n .option(\"--accept-all\", \"Approve mọi pending không hỏi (CI mode)\")\n .option(\"--reject-all\", \"Xóa mọi pending không hỏi\")\n .action(notImplementedYet(\"review\", \"Milestone 08\"));\n}\n","// `avatar scan [--incremental|--full] [--scanners <list>]` — Command 04 spec.\n// Runs 5 project scanners and writes proposals to .claude/_pending/.\n// Implementation deferred — scanner files in src/scanners/ are stubbed.\nimport type { Command } from \"commander\";\nimport { notImplementedYet } from \"../lib/not-implemented-stub.js\";\n\nexport function registerScanCommand(program: Command): void {\n program\n .command(\"scan\")\n .description(\"Chạy project scanner và đề xuất knowledge update (M06)\")\n .option(\"--incremental\", \"Chỉ scan các file thay đổi từ commit cuối\")\n .option(\"--full\", \"Scan toàn bộ dự án (default)\")\n .option(\"--scanners <list>\", \"tech-stack,conventions,architecture,domain,git-pattern\")\n .option(\"--quiet\", \"Chạy ngầm, ít output (dùng cho git hook)\")\n .action(notImplementedYet(\"scan\", \"Milestone 06\"));\n}\n","// `avatar secrets {list,set,get,rm,check}` — Command 13 spec.\n// Backed by OS keychain via @napi-rs/keyring. Service prefix: \"avatar\".\n// Audit log entries are written for set/rm; values NEVER logged.\nimport type { Command } from \"commander\";\nimport { notImplementedYet } from \"../lib/not-implemented-stub.js\";\n\nexport function registerSecretsCommand(program: Command): void {\n const secrets = program.command(\"secrets\").description(\"Quản lý secrets trong OS keychain (M09)\");\n\n secrets\n .command(\"list\")\n .description(\"Liệt kê secrets đã set (chỉ tên, không value)\")\n .action(notImplementedYet(\"secrets list\", \"Milestone 09\"));\n\n secrets\n .command(\"set <service> <name>\")\n .description(\"Set/update secret (prompt ẩn)\")\n .action(notImplementedYet(\"secrets set\", \"Milestone 09\"));\n\n secrets\n .command(\"get <service> <name>\")\n .description(\"Lấy secret, copy clipboard, auto-xóa sau 30s\")\n .action(notImplementedYet(\"secrets get\", \"Milestone 09\"));\n\n secrets\n .command(\"rm <service> <name>\")\n .description(\"Xóa secret khỏi keychain\")\n .action(notImplementedYet(\"secrets rm\", \"Milestone 09\"));\n\n secrets\n .command(\"check\")\n .description(\"Verify mọi secret required bởi MCP đã enabled\")\n .action(notImplementedYet(\"secrets check\", \"Milestone 09\"));\n}\n","// `avatar status [--json]` — Command 06 spec.\n// Read-only snapshot: project name, CLI version, pack version, pending count,\n// backup count, tech-stack first-line. No mutations.\nimport { promises as fs } from \"node:fs\";\nimport { join } from \"node:path\";\nimport boxen from \"boxen\";\nimport type { Command } from \"commander\";\nimport { pathExists, readText } from \"../lib/filesystem-helpers.js\";\nimport { isGitRepo } from \"../lib/git-operations.js\";\nimport { listBackups } from \"../lib/pack-backup-manager.js\";\nimport { readPinnedPackVersion } from \"../lib/team-pack-submodule-manager.js\";\nimport { chalk, log } from \"../lib/terminal-logger.js\";\n\nconst AVATAR_CLI_VERSION = \"1.0.0\";\n\nexport function registerStatusCommand(program: Command): void {\n program\n .command(\"status\")\n .description(\"Snapshot tức thì: project, pack version, pending, backup\")\n .option(\"--json\", \"Output JSON cho script\")\n .action(async (opts: { json?: boolean }) => {\n try {\n const snapshot = await gatherStatus(process.cwd());\n if (opts.json) {\n process.stdout.write(`${JSON.stringify(snapshot, null, 2)}\\n`);\n } else {\n renderStatusBox(snapshot);\n }\n } catch (err) {\n log.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n });\n}\n\ninterface StatusSnapshot {\n projectName: string;\n cliVersion: string;\n packVersion: string | null;\n pendingCount: number;\n backupCount: number;\n techStackSummary: string;\n hasAvatar: boolean;\n}\n\nasync function gatherStatus(cwd: string): Promise<StatusSnapshot> {\n const projectName = cwd.split(\"/\").filter(Boolean).pop() ?? \"unknown\";\n const claudeRoot = join(cwd, \".claude\");\n const hasAvatar = await pathExists(claudeRoot);\n if (!hasAvatar) {\n return {\n projectName,\n cliVersion: AVATAR_CLI_VERSION,\n packVersion: null,\n pendingCount: 0,\n backupCount: 0,\n techStackSummary: \"(Avatar chưa init)\",\n hasAvatar: false,\n };\n }\n\n const packVersion = (await isGitRepo(join(claudeRoot, \"pack\")))\n ? await readPinnedPackVersion(cwd).catch(() => null)\n : null;\n\n const pendingDir = join(claudeRoot, \"_pending\");\n const pendingCount = (await pathExists(pendingDir))\n ? (await fs.readdir(pendingDir)).filter((n) => n.endsWith(\".diff.md\")).length\n : 0;\n\n const backupCount = (await listBackups(cwd)).length;\n\n const techStackSummary = await readTechStackFirstLine(claudeRoot);\n\n return {\n projectName,\n cliVersion: AVATAR_CLI_VERSION,\n packVersion,\n pendingCount,\n backupCount,\n techStackSummary,\n hasAvatar: true,\n };\n}\n\nasync function readTechStackFirstLine(claudeRoot: string): Promise<string> {\n const techStackPath = join(claudeRoot, \"project\", \"tech-stack.md\");\n if (!(await pathExists(techStackPath))) return \"(no tech-stack.md)\";\n const content = await readText(techStackPath);\n const firstNonHeaderLine = content\n .split(\"\\n\")\n .find((l) => l.trim() && !l.startsWith(\"#\") && !l.startsWith(\">\"));\n return firstNonHeaderLine?.trim() ?? \"(empty)\";\n}\n\nfunction renderStatusBox(s: StatusSnapshot): void {\n const lines = [\n `${chalk.bold(\"Avatar Status\")} · ${chalk.cyan(s.projectName)}`,\n \"─\".repeat(48),\n `${chalk.dim(\"CLI version:\")} ${s.cliVersion}`,\n `${chalk.dim(\"Pack version:\")} ${s.packVersion ?? chalk.yellow(\"not installed\")}`,\n `${chalk.dim(\"Pending changes:\")} ${s.pendingCount}${s.pendingCount > 0 ? chalk.dim(\" (avatar review)\") : \"\"}`,\n `${chalk.dim(\"Backups:\")} ${s.backupCount}`,\n `${chalk.dim(\"Tech stack:\")} ${s.techStackSummary}`,\n ];\n process.stdout.write(`${boxen(lines.join(\"\\n\"), { padding: 1, borderStyle: \"round\" })}\\n`);\n}\n","import { promises as fs } from \"node:fs\";\n// Backup .claude/pack/ before `avatar sync --force` so user can `avatar restore`\n// if the sync goes wrong. Naming convention: pack-{currentVersion}-{YYYYMMDD-HHmm}.\nimport { join } from \"node:path\";\nimport { copyDirRecursive, ensureDir, pathExists } from \"./filesystem-helpers.js\";\n\nexport const BACKUP_DIR_NAME = \"_backup\";\n\nfunction timestamp(): string {\n const now = new Date();\n const y = now.getFullYear();\n const m = String(now.getMonth() + 1).padStart(2, \"0\");\n const d = String(now.getDate()).padStart(2, \"0\");\n const h = String(now.getHours()).padStart(2, \"0\");\n const min = String(now.getMinutes()).padStart(2, \"0\");\n return `${y}${m}${d}-${h}${min}`;\n}\n\nexport function buildBackupName(currentVersion: string): string {\n // Strip any leading \"v\" so we don't get pack-vv1.2.3 if someone passes \"v1.2.3\".\n const cleanVersion = currentVersion.replace(/^v/, \"\");\n return `pack-v${cleanVersion}-${timestamp()}`;\n}\n\n// Backup .claude/pack/ to .claude/_backup/{name}/, excluding the submodule's\n// own .git directory (we restore content only, not git history).\nexport async function backupPack(projectRoot: string, currentVersion: string): Promise<string> {\n const name = buildBackupName(currentVersion);\n const srcPath = join(projectRoot, \".claude\", \"pack\");\n const dstPath = join(projectRoot, \".claude\", BACKUP_DIR_NAME, name);\n if (!(await pathExists(srcPath))) {\n throw new Error(\"Không tìm thấy .claude/pack/ để backup\");\n }\n await ensureDir(dstPath);\n await copyDirRecursive(srcPath, dstPath, [\".git\"]);\n return name;\n}\n\nexport async function listBackups(projectRoot: string): Promise<string[]> {\n const dir = join(projectRoot, \".claude\", BACKUP_DIR_NAME);\n if (!(await pathExists(dir))) return [];\n const entries = await fs.readdir(dir, { withFileTypes: true });\n return entries\n .filter((e) => e.isDirectory())\n .map((e) => e.name)\n .sort()\n .reverse();\n}\n","// `avatar sync [--force] [--version <tag>] [--dry-run]` — Command 03 spec.\n// Pulls latest team-ai-pack into .claude/pack. Implementation in next milestone.\nimport type { Command } from \"commander\";\nimport { notImplementedYet } from \"../lib/not-implemented-stub.js\";\n\nexport function registerSyncCommand(program: Command): void {\n program\n .command(\"sync\")\n .description(\"Pull team-ai-pack mới nhất (M08)\")\n .option(\"--force\", \"Override .claude/pack/, backup trước\")\n .option(\"--version <tag>\", \"Pin vào version cụ thể\")\n .option(\"--dry-run\", \"Hiển thị changes, không apply\")\n .action(notImplementedYet(\"sync\", \"Milestone 08\"));\n}\n","// `avatar tools {list,install,remove}` — Commands 10/11/12 spec (Chapter 12 v4).\n// Lifecycle management for system dependencies (git, gh, node, uv, docker) and\n// MCP servers (gitnexus, context7, serena, github, filesystem, playwright).\n// Registry source: team-ai-pack/tools/registry.yaml.\nimport type { Command } from \"commander\";\nimport { notImplementedYet } from \"../lib/not-implemented-stub.js\";\n\nexport function registerToolsCommand(program: Command): void {\n const tools = program.command(\"tools\").description(\"Quản lý system tools + MCP servers (M09)\");\n\n tools\n .command(\"list\")\n .description(\"Liệt kê tool đã cài / còn thiếu\")\n .option(\"--installed\", \"Chỉ liệt kê tool đã cài\")\n .option(\"--missing\", \"Chỉ liệt kê tool còn thiếu\")\n .option(\"--json\", \"Output JSON cho script\")\n .action(notImplementedYet(\"tools list\", \"Milestone 09\"));\n\n tools\n .command(\"install [tool-ids...]\")\n .description(\"Cài tool và đăng ký vào ~/.claude.json\")\n .option(\"--all-recommended\", \"Cài mọi MCP được recommend cho project type\")\n .option(\"--verify\", \"Chạy MCP thử để verify (mất ~30s/tool)\")\n .option(\"--no-secrets\", \"Skip prompt secrets, set sau qua 'avatar secrets'\")\n .action(notImplementedYet(\"tools install\", \"Milestone 09\"));\n\n tools\n .command(\"remove <tool-id>\")\n .description(\"Gỡ tool khỏi ~/.claude.json (optional uninstall binary)\")\n .option(\"--keep-secrets\", \"Không xóa secrets khỏi keychain\")\n .option(\"--keep-binary\", \"Không uninstall npm global binary\")\n .action(notImplementedYet(\"tools remove\", \"Milestone 09\"));\n}\n"],"mappings":"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","names":["program","fs","join","join","join","join","fs","join","dirname","join","dirname","join","join","relative","fs","join","join","fs","program","join","fs","join","boxen","fs","fs","join","join","program","join","boxen","boxen","program","boxen","resolve","program","program","program","program","program","fs","join","boxen","fs","join","join","fs","AVATAR_CLI_VERSION","program","join","fs","boxen","program","program"]}
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@nalvietnam/avatar-cli",
3
+ "version": "1.0.0",
4
+ "description": "AI harness CLI for NAL Vietnam engineering",
5
+ "type": "module",
6
+ "bin": {
7
+ "avatar": "./bin/avatar.js"
8
+ },
9
+ "files": ["bin", "dist", "src/templates", "src/hooks", "README.md"],
10
+ "engines": {
11
+ "node": ">=18.17.0"
12
+ },
13
+ "scripts": {
14
+ "build": "tsup",
15
+ "dev": "tsup --watch",
16
+ "test": "vitest run --passWithNoTests",
17
+ "test:watch": "vitest",
18
+ "lint": "biome check src test",
19
+ "format": "biome format --write src test",
20
+ "prepublishOnly": "npm run build && npm run test"
21
+ },
22
+ "dependencies": {
23
+ "commander": "^12.0.0",
24
+ "@inquirer/prompts": "^5.0.0",
25
+ "chalk": "^5.3.0",
26
+ "ora": "^8.0.0",
27
+ "cli-table3": "^0.6.3",
28
+ "boxen": "^8.0.0",
29
+ "diff": "^5.2.0",
30
+ "cli-highlight": "^2.1.11",
31
+ "simple-git": "^3.22.0",
32
+ "fast-glob": "^3.3.2",
33
+ "zod": "^3.22.4",
34
+ "@napi-rs/keyring": "^1.1.0",
35
+ "open": "^10.0.0",
36
+ "clipboardy": "^4.0.0"
37
+ },
38
+ "devDependencies": {
39
+ "@biomejs/biome": "^1.5.0",
40
+ "@types/node": "^20.11.0",
41
+ "@types/diff": "^5.0.9",
42
+ "tsup": "^8.0.0",
43
+ "typescript": "^5.3.0",
44
+ "vitest": "^1.2.0"
45
+ }
46
+ }
@@ -0,0 +1,24 @@
1
+ #!/bin/sh
2
+ # Avatar post-merge git hook
3
+ # Auto-runs `avatar scan --incremental` after `git pull` / `git merge`.
4
+ # Installed by `avatar init`. Uninstall by deleting .git/hooks/post-merge.
5
+
6
+ # Skip if avatar CLI is not on PATH.
7
+ if ! command -v avatar >/dev/null 2>&1; then
8
+ exit 0
9
+ fi
10
+
11
+ # Skip if this project doesn't have an Avatar pack.
12
+ if [ ! -d ".claude/pack" ]; then
13
+ exit 0
14
+ fi
15
+
16
+ # Background scan — never block git.
17
+ (avatar scan --incremental --quiet &) >/dev/null 2>&1
18
+
19
+ echo ""
20
+ echo "🔍 Avatar đang scan thay đổi ngầm. Khi xong sẽ thông báo."
21
+ echo " Manual: avatar review"
22
+ echo ""
23
+
24
+ exit 0
@@ -0,0 +1,33 @@
1
+ #!/bin/sh
2
+ # Avatar pre-push hook (CLIENT MODE ONLY)
3
+ # Installed in src/.git/hooks/pre-push when `avatar init --mode=client`.
4
+ # Purpose: block accidental leak of Avatar files into the client repo.
5
+ #
6
+ # Rationale: in client mode, src/ is the client's submodule; Avatar files live
7
+ # in the workspace ABOVE src/. If a dev accidentally `git add` an Avatar-owned
8
+ # path inside src/, this hook stops the push and explains the fix.
9
+
10
+ # Patterns that must never appear in a client-repo push.
11
+ FORBIDDEN_PATTERNS='\.claude/|^CLAUDE\.md$|\.avatar/'
12
+
13
+ # Check files staged in commits being pushed.
14
+ leaked=$(git diff --cached --name-only HEAD 2>/dev/null | grep -E "$FORBIDDEN_PATTERNS" || true)
15
+ if [ -z "$leaked" ]; then
16
+ # Also check files in already-committed-but-not-yet-pushed history.
17
+ leaked=$(git log --name-only --format= origin/HEAD..HEAD 2>/dev/null | grep -E "$FORBIDDEN_PATTERNS" || true)
18
+ fi
19
+
20
+ if [ -n "$leaked" ]; then
21
+ echo ""
22
+ echo "✗ Avatar pre-push hook: phát hiện Avatar files trong client push:"
23
+ echo ""
24
+ echo "$leaked" | sed 's/^/ /'
25
+ echo ""
26
+ echo "Avatar files (.claude/, CLAUDE.md) phải commit vào WORKSPACE, không phải client repo."
27
+ echo "Dùng: avatar commit --avatar"
28
+ echo "Bypass (không khuyến nghị): git push --no-verify"
29
+ echo ""
30
+ exit 1
31
+ fi
32
+
33
+ exit 0
@@ -0,0 +1,40 @@
1
+ # {{projectName}}
2
+
3
+ {{projectDescription}}
4
+
5
+ ## Context cho Claude Code
6
+
7
+ File này là entry point. Claude Code đọc file này và resolve các @-imports bên dưới
8
+ để có toàn bộ context dự án. KHÔNG sửa file này thủ công; chạy `avatar scan`
9
+ và `avatar review` để cập nhật.
10
+
11
+ ### Knowledge nền tảng (từ team-ai-pack)
12
+
13
+ @.claude/pack/knowledge/org/coding-standards.md
14
+ @.claude/pack/knowledge/org/git-workflow.md
15
+ @.claude/pack/knowledge/org/security-policy.md
16
+
17
+ ### Knowledge dự án này (auto-scan)
18
+
19
+ @.claude/project/tech-stack.md
20
+ @.claude/project/conventions.md
21
+ @.claude/project/architecture.md
22
+ @.claude/project/domain.md
23
+ @.claude/project/gotchas.md
24
+
25
+ ### Hướng dẫn cho Claude
26
+
27
+ - Tuân thủ convention trong @.claude/project/conventions.md trước, sau đó tới
28
+ @.claude/pack/knowledge/org/coding-standards.md.
29
+ - Khi tạo file mới, đặt theo pattern trong @.claude/project/architecture.md.
30
+ - Khi gặp gotcha mới, đề xuất ghi vào @.claude/project/gotchas.md.
31
+ - Câu hỏi quy trình: đọc @.claude/pack/knowledge/playbooks/
32
+ - Quyết định kiến trúc lớn: tham khảo @.claude/pack/knowledge/decisions/
33
+
34
+ ### Project metadata
35
+
36
+ - Team owner: {{teamOwner}}
37
+ - Avatar version: {{avatarVersion}}
38
+ - Pack version: {{packVersion}}
39
+ - Last scan: {{lastScan}}
40
+ - Mode: {{mode}}
@@ -0,0 +1,4 @@
1
+ # Avatar — git-ignored entries injected on `avatar init`
2
+ .claude/_pending/
3
+ .claude/_backup/
4
+ .claude/state/session-*.json
@@ -0,0 +1,27 @@
1
+ # Architecture — {{projectName}}
2
+
3
+ > File này do `avatar scan` cập nhật. KHÔNG sửa thủ công.
4
+
5
+ ## Tổng quan kiến trúc
6
+
7
+ {{architectureOverview}}
8
+
9
+ ## Layer / Module chính
10
+
11
+ {{moduleLayout}}
12
+
13
+ ## Data flow
14
+
15
+ {{dataFlow}}
16
+
17
+ ## External integration
18
+
19
+ {{externalIntegrations}}
20
+
21
+ ## Deployment topology
22
+
23
+ {{deploymentTopology}}
24
+
25
+ ---
26
+
27
+ *Generated: {{lastScan}} · Avatar v{{avatarVersion}}*
@@ -0,0 +1,27 @@
1
+ # Conventions — {{projectName}}
2
+
3
+ > File này do `avatar scan` cập nhật. KHÔNG sửa thủ công.
4
+
5
+ ## Code style
6
+
7
+ {{codeStyle}}
8
+
9
+ ## Naming convention
10
+
11
+ {{namingConvention}}
12
+
13
+ ## Folder structure
14
+
15
+ {{folderStructure}}
16
+
17
+ ## Commit message
18
+
19
+ {{commitConvention}}
20
+
21
+ ## Linter / Formatter
22
+
23
+ {{linterConfig}}
24
+
25
+ ---
26
+
27
+ *Generated: {{lastScan}} · Avatar v{{avatarVersion}}*
@@ -0,0 +1,23 @@
1
+ # Domain — {{projectName}}
2
+
3
+ > File này do `avatar scan` cập nhật. KHÔNG sửa thủ công.
4
+
5
+ ## Mô tả domain
6
+
7
+ {{domainDescription}}
8
+
9
+ ## Khái niệm chính
10
+
11
+ {{coreEntities}}
12
+
13
+ ## Use case chính
14
+
15
+ {{primaryUseCases}}
16
+
17
+ ## Glossary
18
+
19
+ {{domainGlossary}}
20
+
21
+ ---
22
+
23
+ *Generated: {{lastScan}} · Avatar v{{avatarVersion}}*
@@ -0,0 +1,28 @@
1
+ # Gotchas — {{projectName}}
2
+
3
+ > File tự đầy theo thời gian qua skill `knowledge-capture`. Bẫy quan trọng,
4
+ > bài học kinh nghiệm sẽ tích lũy ở đây.
5
+
6
+ ## Cấu trúc một mục gotcha
7
+
8
+ ```
9
+ ### [Tag] Tên ngắn gọn
10
+
11
+ **Triệu chứng:** Lỗi gì xuất hiện.
12
+
13
+ **Nguyên nhân:** Vì sao xảy ra.
14
+
15
+ **Cách tránh:** Làm thế nào để không lặp lại.
16
+
17
+ **Tham khảo:** Link PR, issue, commit.
18
+ ```
19
+
20
+ ---
21
+
22
+ ## Bẫy đã ghi nhận
23
+
24
+ *(Chưa có gotcha nào — Avatar sẽ tự bổ sung khi gặp pattern lặp lại trong session.)*
25
+
26
+ ---
27
+
28
+ *Last updated: {{lastScan}} · Avatar v{{avatarVersion}}*
@@ -0,0 +1,32 @@
1
+ # Tech stack — {{projectName}}
2
+
3
+ > File này do `avatar scan` cập nhật. KHÔNG sửa thủ công. Đề xuất thay đổi
4
+ > qua `avatar review`.
5
+
6
+ ## Ngôn ngữ chính
7
+
8
+ {{primaryLanguage}}
9
+
10
+ ## Framework / Library
11
+
12
+ {{frameworks}}
13
+
14
+ ## Database
15
+
16
+ {{databases}}
17
+
18
+ ## Test stack
19
+
20
+ {{testStack}}
21
+
22
+ ## Build & deploy
23
+
24
+ {{buildStack}}
25
+
26
+ ## Phiên bản công cụ
27
+
28
+ {{toolVersions}}
29
+
30
+ ---
31
+
32
+ *Generated: {{lastScan}} · Avatar v{{avatarVersion}}*
@@ -0,0 +1,32 @@
1
+ {
2
+ "allowedTools": [
3
+ "Bash(npm:*)",
4
+ "Bash(pnpm:*)",
5
+ "Bash(git:*)",
6
+ "Bash(node:*)",
7
+ "Edit",
8
+ "Read",
9
+ "Write",
10
+ "Glob",
11
+ "Grep"
12
+ ],
13
+ "hooks": {
14
+ "PostToolUse": [
15
+ {
16
+ "matcher": "Edit|Write",
17
+ "hooks": [
18
+ {
19
+ "type": "command",
20
+ "command": "avatar internal post-edit",
21
+ "timeout": 5
22
+ }
23
+ ]
24
+ }
25
+ ]
26
+ },
27
+ "env": {
28
+ "AVATAR_PROJECT": "{{projectName}}",
29
+ "AVATAR_OWNER": "{{teamOwner}}",
30
+ "AVATAR_MODE": "{{mode}}"
31
+ }
32
+ }