@nalvietnam/avatar-cli 1.4.2 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +386 -33
- package/dist/index.js.map +1 -1
- package/dist/lib/print-welcome-screen.js +1 -1
- package/dist/lib/print-welcome-screen.js.map +1 -1
- package/dist/templates/CLAUDE.md.tpl +49 -3
- package/dist/templates/settings.json.tpl +40 -26
- package/package.json +1 -1
- package/src/templates/CLAUDE.md.tpl +49 -3
- package/src/templates/settings.json.tpl +40 -26
|
@@ -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.
|
|
87
|
+
var CLI_VERSION = "1.5.0";
|
|
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.4.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// Long-running spinner với elapsed time — update text mỗi giây.\n// Trả về { stop } để caller clear interval + stop spinner đúng cách.\n// Format: \"⠋ <prefix> (1:23)\" — user biết tool alive + chạy bao lâu.\nexport function spinnerWithElapsed(prefix: string): {\n succeed: (text: string) => void;\n fail: (text: string) => void;\n stop: () => void;\n} {\n const startMs = Date.now();\n const sp = spinner(`${prefix} (0:00)`);\n const formatElapsed = (): string => {\n const sec = Math.floor((Date.now() - startMs) / 1000);\n const m = Math.floor(sec / 60);\n const s = sec % 60;\n return `${m}:${String(s).padStart(2, \"0\")}`;\n };\n const interval = setInterval(() => {\n sp.text = `${prefix} (${formatElapsed()})`;\n }, 1000);\n return {\n succeed: (text: string) => {\n clearInterval(interval);\n sp.succeed(`${text} (${formatElapsed()})`);\n },\n fail: (text: string) => {\n clearInterval(interval);\n sp.fail(`${text} (${formatElapsed()})`);\n },\n stop: () => {\n clearInterval(interval);\n sp.stop();\n },\n };\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.5.0\";\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// Long-running spinner với elapsed time — update text mỗi giây.\n// Trả về { stop } để caller clear interval + stop spinner đúng cách.\n// Format: \"⠋ <prefix> (1:23)\" — user biết tool alive + chạy bao lâu.\nexport function spinnerWithElapsed(prefix: string): {\n succeed: (text: string) => void;\n fail: (text: string) => void;\n stop: () => void;\n} {\n const startMs = Date.now();\n const sp = spinner(`${prefix} (0:00)`);\n const formatElapsed = (): string => {\n const sec = Math.floor((Date.now() - startMs) / 1000);\n const m = Math.floor(sec / 60);\n const s = sec % 60;\n return `${m}:${String(s).padStart(2, \"0\")}`;\n };\n const interval = setInterval(() => {\n sp.text = `${prefix} (${formatElapsed()})`;\n }, 1000);\n return {\n succeed: (text: string) => {\n clearInterval(interval);\n sp.succeed(`${text} (${formatElapsed()})`);\n },\n fail: (text: string) => {\n clearInterval(interval);\n sp.fail(`${text} (${formatElapsed()})`);\n },\n stop: () => {\n clearInterval(interval);\n sp.stop();\n },\n };\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"]}
|
|
@@ -4,9 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
## Context cho Claude Code
|
|
6
6
|
|
|
7
|
-
File này là entry point cho Claude Code khi làm việc trong workspace.
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
File này là entry point cho Claude Code khi làm việc trong workspace.
|
|
8
|
+
|
|
9
|
+
Workspace có **team-ai-pack** active — bộ skills, agents, slash commands, hooks, workflows, scripts do team NAL build sẵn và share chung. Pack được mount vào các thư mục convention của Claude Code qua symlink, nên Claude Code tự thấy + load mọi capability từ pack mà không cần thao tác thêm.
|
|
10
|
+
|
|
11
|
+
→ Khi user yêu cầu việc gì khớp với skill/command/agent có sẵn, **ưu tiên dùng pack** thay vì làm thủ công. Xem `.claude/pack/` để biết catalog đầy đủ.
|
|
10
12
|
|
|
11
13
|
### ⚠️ COMMIT CONVENTIONS — BẮT BUỘC TUÂN THỦ
|
|
12
14
|
|
|
@@ -55,6 +57,50 @@ Nếu bạn cố tình muốn share knowledge cập nhật (vd ADR mới, gotcha
|
|
|
55
57
|
- `git commit` raw không biết 2-repo layout → dễ commit nhầm vào wrong remote
|
|
56
58
|
- AI agent tự discard untracked files = **mất data im lặng** (file backup, ADR draft, gotcha note user đang viết dở)
|
|
57
59
|
|
|
60
|
+
### 🧰 Team AI Pack
|
|
61
|
+
|
|
62
|
+
Workspace dùng **team-ai-pack** (git submodule tại `.claude/pack/`) làm nguồn chung cho skills, agents, commands, hooks, workflows, scripts. Mount points qua symlink:
|
|
63
|
+
|
|
64
|
+
| Convention path Claude Code scan | Source thực |
|
|
65
|
+
|---|---|
|
|
66
|
+
| `.claude/skills/` | `.claude/pack/skills/` |
|
|
67
|
+
| `.claude/agents/` | `.claude/pack/agents/` |
|
|
68
|
+
| `.claude/commands/` | `.claude/pack/commands/` |
|
|
69
|
+
| `.claude/hooks/` | `.claude/pack/hooks/` |
|
|
70
|
+
| `.claude/workflows/` | `.claude/pack/workflows/` |
|
|
71
|
+
| `.claude/scripts/` | `.claude/pack/scripts/` |
|
|
72
|
+
|
|
73
|
+
→ Bất kỳ skill/agent/command nào team push lên `team-ai-pack` đều available ngay khi member chạy `avatar sync`. Không cần copy file, không cần restart Claude Code.
|
|
74
|
+
|
|
75
|
+
**Cách sử dụng**:
|
|
76
|
+
|
|
77
|
+
- **Slash commands**: gõ `/<name>` để invoke (vd `/review`, `/plan`) — register tự động từ `.claude/commands/<name>.md`
|
|
78
|
+
- **Skills**: agent tự activate dựa trên context match với skill description, hoặc user explicit ask ("activate skill X")
|
|
79
|
+
- **Agents (subagents)**: spawn qua Task tool với `subagent_type` khớp tên file `.claude/agents/<name>.md`
|
|
80
|
+
|
|
81
|
+
**Update pack**:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
avatar sync # Pull latest tag
|
|
85
|
+
avatar sync --version v0.3.0 # Pin specific version
|
|
86
|
+
avatar sync --dry-run # Preview changes trước khi sync
|
|
87
|
+
avatar sync --force # Backup real dirs đang chiếm chỗ rồi symlink
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Check pack hiện tại**:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
avatar status # Show pack version + sync status
|
|
94
|
+
ls -la .claude/skills # Verify symlink → pack/skills
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
> ⚠️ **Hướng dẫn dành riêng cho AI assistant (Claude Code agent):**
|
|
98
|
+
>
|
|
99
|
+
> 1. **Trước khi tự code 1 task**, check xem `.claude/pack/skills/`, `.claude/pack/commands/`, `.claude/pack/agents/` có capability sẵn không. Nếu có → dùng, không làm lại.
|
|
100
|
+
> 2. **KHÔNG edit file trong `.claude/pack/`** — đó là submodule team-shared, edit trực tiếp sẽ bị conflict khi sync. Muốn đổi pack content → báo team admin update upstream.
|
|
101
|
+
> 3. **KHÔNG xóa symlink** ở `.claude/{skills,agents,commands,...}` — chúng là entry point cho Claude Code. Nếu user lỡ xóa → recover bằng `avatar sync --force`.
|
|
102
|
+
> 4. Nếu pack thiếu skill/command cần thiết → suggest user đề xuất với team admin add vào pack, không tự tạo skill local trong `.claude/skills/` (sẽ collision với symlink khi sync sau).
|
|
103
|
+
|
|
58
104
|
{{gitnexusSection}}
|
|
59
105
|
|
|
60
106
|
### Project metadata
|
|
@@ -1,32 +1,46 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
|
|
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
|
-
},
|
|
2
|
+
"$schema": "https://json.schemastore.org/claude-code-settings.json",
|
|
3
|
+
"includeCoAuthoredBy": false,
|
|
27
4
|
"env": {
|
|
28
5
|
"AVATAR_PROJECT": "{{projectName}}",
|
|
29
6
|
"AVATAR_OWNER": "{{teamOwner}}",
|
|
30
7
|
"AVATAR_MODE": "{{mode}}"
|
|
31
|
-
}
|
|
8
|
+
},
|
|
9
|
+
"permissions": {
|
|
10
|
+
"allow": [
|
|
11
|
+
"Read(**)",
|
|
12
|
+
"Glob(**)",
|
|
13
|
+
"Grep(**)",
|
|
14
|
+
"Bash(pnpm test*)",
|
|
15
|
+
"Bash(pnpm lint*)",
|
|
16
|
+
"Bash(pnpm build*)",
|
|
17
|
+
"Bash(pnpm dev*)",
|
|
18
|
+
"Bash(npm test*)",
|
|
19
|
+
"Bash(npm run*)",
|
|
20
|
+
"Bash(git status*)",
|
|
21
|
+
"Bash(git diff*)",
|
|
22
|
+
"Bash(git log*)",
|
|
23
|
+
"Bash(git add*)",
|
|
24
|
+
"Bash(git commit*)",
|
|
25
|
+
"Bash(git branch*)",
|
|
26
|
+
"Bash(git checkout*)",
|
|
27
|
+
"Bash(gh pr*)",
|
|
28
|
+
"Bash(gh issue*)"
|
|
29
|
+
],
|
|
30
|
+
"deny": [
|
|
31
|
+
"Read(.env)",
|
|
32
|
+
"Read(.env.*)",
|
|
33
|
+
"Read(~/.ssh/**)",
|
|
34
|
+
"Read(~/.aws/**)",
|
|
35
|
+
"Read(~/.config/gh/**)",
|
|
36
|
+
"Bash(rm -rf /*)",
|
|
37
|
+
"Bash(rm -rf ~*)",
|
|
38
|
+
"Bash(git push --force*)",
|
|
39
|
+
"Bash(git push --no-verify*)",
|
|
40
|
+
"Bash(git commit --no-verify*)",
|
|
41
|
+
"Bash(curl * | sh*)",
|
|
42
|
+
"Bash(curl * | bash*)"
|
|
43
|
+
]
|
|
44
|
+
},
|
|
45
|
+
"hooks": {}
|
|
32
46
|
}
|
package/package.json
CHANGED
|
@@ -4,9 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
## Context cho Claude Code
|
|
6
6
|
|
|
7
|
-
File này là entry point cho Claude Code khi làm việc trong workspace.
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
File này là entry point cho Claude Code khi làm việc trong workspace.
|
|
8
|
+
|
|
9
|
+
Workspace có **team-ai-pack** active — bộ skills, agents, slash commands, hooks, workflows, scripts do team NAL build sẵn và share chung. Pack được mount vào các thư mục convention của Claude Code qua symlink, nên Claude Code tự thấy + load mọi capability từ pack mà không cần thao tác thêm.
|
|
10
|
+
|
|
11
|
+
→ Khi user yêu cầu việc gì khớp với skill/command/agent có sẵn, **ưu tiên dùng pack** thay vì làm thủ công. Xem `.claude/pack/` để biết catalog đầy đủ.
|
|
10
12
|
|
|
11
13
|
### ⚠️ COMMIT CONVENTIONS — BẮT BUỘC TUÂN THỦ
|
|
12
14
|
|
|
@@ -55,6 +57,50 @@ Nếu bạn cố tình muốn share knowledge cập nhật (vd ADR mới, gotcha
|
|
|
55
57
|
- `git commit` raw không biết 2-repo layout → dễ commit nhầm vào wrong remote
|
|
56
58
|
- AI agent tự discard untracked files = **mất data im lặng** (file backup, ADR draft, gotcha note user đang viết dở)
|
|
57
59
|
|
|
60
|
+
### 🧰 Team AI Pack
|
|
61
|
+
|
|
62
|
+
Workspace dùng **team-ai-pack** (git submodule tại `.claude/pack/`) làm nguồn chung cho skills, agents, commands, hooks, workflows, scripts. Mount points qua symlink:
|
|
63
|
+
|
|
64
|
+
| Convention path Claude Code scan | Source thực |
|
|
65
|
+
|---|---|
|
|
66
|
+
| `.claude/skills/` | `.claude/pack/skills/` |
|
|
67
|
+
| `.claude/agents/` | `.claude/pack/agents/` |
|
|
68
|
+
| `.claude/commands/` | `.claude/pack/commands/` |
|
|
69
|
+
| `.claude/hooks/` | `.claude/pack/hooks/` |
|
|
70
|
+
| `.claude/workflows/` | `.claude/pack/workflows/` |
|
|
71
|
+
| `.claude/scripts/` | `.claude/pack/scripts/` |
|
|
72
|
+
|
|
73
|
+
→ Bất kỳ skill/agent/command nào team push lên `team-ai-pack` đều available ngay khi member chạy `avatar sync`. Không cần copy file, không cần restart Claude Code.
|
|
74
|
+
|
|
75
|
+
**Cách sử dụng**:
|
|
76
|
+
|
|
77
|
+
- **Slash commands**: gõ `/<name>` để invoke (vd `/review`, `/plan`) — register tự động từ `.claude/commands/<name>.md`
|
|
78
|
+
- **Skills**: agent tự activate dựa trên context match với skill description, hoặc user explicit ask ("activate skill X")
|
|
79
|
+
- **Agents (subagents)**: spawn qua Task tool với `subagent_type` khớp tên file `.claude/agents/<name>.md`
|
|
80
|
+
|
|
81
|
+
**Update pack**:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
avatar sync # Pull latest tag
|
|
85
|
+
avatar sync --version v0.3.0 # Pin specific version
|
|
86
|
+
avatar sync --dry-run # Preview changes trước khi sync
|
|
87
|
+
avatar sync --force # Backup real dirs đang chiếm chỗ rồi symlink
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Check pack hiện tại**:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
avatar status # Show pack version + sync status
|
|
94
|
+
ls -la .claude/skills # Verify symlink → pack/skills
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
> ⚠️ **Hướng dẫn dành riêng cho AI assistant (Claude Code agent):**
|
|
98
|
+
>
|
|
99
|
+
> 1. **Trước khi tự code 1 task**, check xem `.claude/pack/skills/`, `.claude/pack/commands/`, `.claude/pack/agents/` có capability sẵn không. Nếu có → dùng, không làm lại.
|
|
100
|
+
> 2. **KHÔNG edit file trong `.claude/pack/`** — đó là submodule team-shared, edit trực tiếp sẽ bị conflict khi sync. Muốn đổi pack content → báo team admin update upstream.
|
|
101
|
+
> 3. **KHÔNG xóa symlink** ở `.claude/{skills,agents,commands,...}` — chúng là entry point cho Claude Code. Nếu user lỡ xóa → recover bằng `avatar sync --force`.
|
|
102
|
+
> 4. Nếu pack thiếu skill/command cần thiết → suggest user đề xuất với team admin add vào pack, không tự tạo skill local trong `.claude/skills/` (sẽ collision với symlink khi sync sau).
|
|
103
|
+
|
|
58
104
|
{{gitnexusSection}}
|
|
59
105
|
|
|
60
106
|
### Project metadata
|
|
@@ -1,32 +1,46 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
|
|
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
|
-
},
|
|
2
|
+
"$schema": "https://json.schemastore.org/claude-code-settings.json",
|
|
3
|
+
"includeCoAuthoredBy": false,
|
|
27
4
|
"env": {
|
|
28
5
|
"AVATAR_PROJECT": "{{projectName}}",
|
|
29
6
|
"AVATAR_OWNER": "{{teamOwner}}",
|
|
30
7
|
"AVATAR_MODE": "{{mode}}"
|
|
31
|
-
}
|
|
8
|
+
},
|
|
9
|
+
"permissions": {
|
|
10
|
+
"allow": [
|
|
11
|
+
"Read(**)",
|
|
12
|
+
"Glob(**)",
|
|
13
|
+
"Grep(**)",
|
|
14
|
+
"Bash(pnpm test*)",
|
|
15
|
+
"Bash(pnpm lint*)",
|
|
16
|
+
"Bash(pnpm build*)",
|
|
17
|
+
"Bash(pnpm dev*)",
|
|
18
|
+
"Bash(npm test*)",
|
|
19
|
+
"Bash(npm run*)",
|
|
20
|
+
"Bash(git status*)",
|
|
21
|
+
"Bash(git diff*)",
|
|
22
|
+
"Bash(git log*)",
|
|
23
|
+
"Bash(git add*)",
|
|
24
|
+
"Bash(git commit*)",
|
|
25
|
+
"Bash(git branch*)",
|
|
26
|
+
"Bash(git checkout*)",
|
|
27
|
+
"Bash(gh pr*)",
|
|
28
|
+
"Bash(gh issue*)"
|
|
29
|
+
],
|
|
30
|
+
"deny": [
|
|
31
|
+
"Read(.env)",
|
|
32
|
+
"Read(.env.*)",
|
|
33
|
+
"Read(~/.ssh/**)",
|
|
34
|
+
"Read(~/.aws/**)",
|
|
35
|
+
"Read(~/.config/gh/**)",
|
|
36
|
+
"Bash(rm -rf /*)",
|
|
37
|
+
"Bash(rm -rf ~*)",
|
|
38
|
+
"Bash(git push --force*)",
|
|
39
|
+
"Bash(git push --no-verify*)",
|
|
40
|
+
"Bash(git commit --no-verify*)",
|
|
41
|
+
"Bash(curl * | sh*)",
|
|
42
|
+
"Bash(curl * | bash*)"
|
|
43
|
+
]
|
|
44
|
+
},
|
|
45
|
+
"hooks": {}
|
|
32
46
|
}
|