@nalvietnam/avatar-cli 1.12.2 → 1.13.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 +102 -5426
- package/dist/index.js.map +1 -1
- package/dist/lib/print-welcome-screen.js +9 -138
- package/dist/lib/print-welcome-screen.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,140 +1,11 @@
|
|
|
1
1
|
// @nalvietnam/avatar-cli — built with tsup
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
"\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D",
|
|
12
|
-
"\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u255A\u2588\u2588\u2557 \u2588\u2588\u2554\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557",
|
|
13
|
-
"\u2588\u2588\u2551 \u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551",
|
|
14
|
-
"\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D"
|
|
15
|
-
];
|
|
16
|
-
var GRADIENT_STOPS = [
|
|
17
|
-
[217, 79, 30],
|
|
18
|
-
// cam-cháy (#d94f1e)
|
|
19
|
-
[200, 70, 80],
|
|
20
|
-
// cam-hồng
|
|
21
|
-
[170, 70, 140],
|
|
22
|
-
// hồng-tím
|
|
23
|
-
[125, 88, 217]
|
|
24
|
-
// tím (#7d58d9)
|
|
25
|
-
];
|
|
26
|
-
function lerpChannel(a, b, t) {
|
|
27
|
-
return Math.round(a + (b - a) * t);
|
|
28
|
-
}
|
|
29
|
-
function gradientAt(t) {
|
|
30
|
-
const clamped = Math.max(0, Math.min(1, t));
|
|
31
|
-
const scaled = clamped * (GRADIENT_STOPS.length - 1);
|
|
32
|
-
const lo = Math.floor(scaled);
|
|
33
|
-
const hi = Math.min(GRADIENT_STOPS.length - 1, lo + 1);
|
|
34
|
-
const localT = scaled - lo;
|
|
35
|
-
const a = GRADIENT_STOPS[lo];
|
|
36
|
-
const b = GRADIENT_STOPS[hi];
|
|
37
|
-
return [
|
|
38
|
-
lerpChannel(a[0], b[0], localT),
|
|
39
|
-
lerpChannel(a[1], b[1], localT),
|
|
40
|
-
lerpChannel(a[2], b[2], localT)
|
|
41
|
-
];
|
|
42
|
-
}
|
|
43
|
-
function renderAvatarBanner(opts) {
|
|
44
|
-
const isTty = process.stdout.isTTY ?? false;
|
|
45
|
-
const supportsColor = isTty && chalk.level > 0;
|
|
46
|
-
if (!supportsColor) {
|
|
47
|
-
return [...BANNER_LINES, ...opts?.tagline ? ["", opts.tagline] : []].join("\n");
|
|
48
|
-
}
|
|
49
|
-
const colored = BANNER_LINES.map((line, idx) => {
|
|
50
|
-
const t = BANNER_LINES.length === 1 ? 0 : idx / (BANNER_LINES.length - 1);
|
|
51
|
-
const [r, g, b] = gradientAt(t);
|
|
52
|
-
return chalk.rgb(r, g, b).bold(line);
|
|
53
|
-
});
|
|
54
|
-
if (opts?.tagline) {
|
|
55
|
-
colored.push("");
|
|
56
|
-
colored.push(chalk.dim(opts.tagline));
|
|
57
|
-
}
|
|
58
|
-
return colored.join("\n");
|
|
59
|
-
}
|
|
60
|
-
function printAvatarBanner(opts) {
|
|
61
|
-
process.stdout.write(`
|
|
62
|
-
${renderAvatarBanner(opts)}
|
|
63
|
-
|
|
64
|
-
`);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// src/lib/read-cli-version-from-package-json.ts
|
|
68
|
-
import { readFileSync } from "fs";
|
|
69
|
-
import { dirname, resolve } from "path";
|
|
70
|
-
import { fileURLToPath } from "url";
|
|
71
|
-
var cachedVersion = null;
|
|
72
|
-
function readCliVersion() {
|
|
73
|
-
if (cachedVersion !== null) return cachedVersion;
|
|
74
|
-
const here = dirname(fileURLToPath(import.meta.url));
|
|
75
|
-
for (let i = 0; i < 5; i++) {
|
|
76
|
-
const candidate = resolve(here, ...Array(i).fill(".."), "package.json");
|
|
77
|
-
try {
|
|
78
|
-
const raw = readFileSync(candidate, "utf8");
|
|
79
|
-
const pkg = JSON.parse(raw);
|
|
80
|
-
if (pkg.name === "@nalvietnam/avatar-cli" && typeof pkg.version === "string") {
|
|
81
|
-
cachedVersion = pkg.version;
|
|
82
|
-
return cachedVersion;
|
|
83
|
-
}
|
|
84
|
-
} catch {
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
cachedVersion = "unknown";
|
|
88
|
-
return cachedVersion;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// src/lib/terminal-logger.ts
|
|
92
|
-
import chalk2 from "chalk";
|
|
93
|
-
import ora from "ora";
|
|
94
|
-
|
|
95
|
-
// src/lib/print-welcome-screen.ts
|
|
96
|
-
var COMMANDS = [
|
|
97
|
-
{ name: "login", desc: "\u0110\u0103ng nh\u1EADp Google SSO (@nal.vn)" },
|
|
98
|
-
{ name: "init", desc: "Kh\u1EDFi t\u1EA1o Avatar \u2014 3 flow t\u1EF1 nh\u1EADn di\u1EC7n" },
|
|
99
|
-
{ name: "sync", desc: "Pull team-ai-pack m\u1EDBi nh\u1EA5t" },
|
|
100
|
-
{ name: "scan", desc: "Project scanner + knowledge proposal" },
|
|
101
|
-
{ name: "review", desc: "Duy\u1EC7t pending proposals" },
|
|
102
|
-
{ name: "status", desc: "Snapshot project, pack, pending, backup" },
|
|
103
|
-
{ name: "doctor", desc: "Ch\u1EA9n \u0111o\xE1n c\xE0i \u0111\u1EB7t Avatar" },
|
|
104
|
-
{ name: "restore", desc: "Kh\xF4i ph\u1EE5c .claude/pack/ t\u1EEB backup" },
|
|
105
|
-
{ name: "commit", desc: "Commit src/ ho\u1EB7c Avatar state (client mode)" },
|
|
106
|
-
{ name: "tools", desc: "Qu\u1EA3n l\xFD system tools + MCP servers" },
|
|
107
|
-
{ name: "secrets", desc: "Qu\u1EA3n l\xFD secrets trong OS keychain" },
|
|
108
|
-
{ name: "uninstall", desc: "G\u1EE1 Avatar kh\u1ECFi project + backup" },
|
|
109
|
-
{ name: "help", desc: "Hi\u1EC3n th\u1ECB help cho t\u1EEBng l\u1EC7nh" }
|
|
110
|
-
];
|
|
111
|
-
function printWelcomeScreen() {
|
|
112
|
-
printAvatarBanner({ tagline: `v${readCliVersion()} \xB7 AI harness CLI for NAL Vietnam` });
|
|
113
|
-
const maxNameWidth = Math.max(...COMMANDS.map((c) => c.name.length));
|
|
114
|
-
const commandLines = COMMANDS.map((c) => {
|
|
115
|
-
const padded = c.name.padEnd(maxNameWidth);
|
|
116
|
-
return ` ${chalk2.cyan(padded)} ${chalk2.dim(c.desc)}`;
|
|
117
|
-
}).join("\n");
|
|
118
|
-
const nextSteps = [
|
|
119
|
-
"",
|
|
120
|
-
chalk2.bold("13 l\u1EC7nh s\u1EB5n s\xE0ng:"),
|
|
121
|
-
"",
|
|
122
|
-
commandLines,
|
|
123
|
-
"",
|
|
124
|
-
chalk2.bold("Next steps:"),
|
|
125
|
-
"",
|
|
126
|
-
` ${chalk2.green("1.")} ${chalk2.cyan("avatar login")} \u0110\u0103ng nh\u1EADp SSO`,
|
|
127
|
-
` ${chalk2.green("2.")} ${chalk2.cyan("avatar init")} Kh\u1EDFi t\u1EA1o d\u1EF1 \xE1n`,
|
|
128
|
-
` ${chalk2.green("?.")} ${chalk2.cyan("avatar <command> --help")} Xem chi ti\u1EBFt l\u1EC7nh`,
|
|
129
|
-
"",
|
|
130
|
-
chalk2.dim("Docs: https://www.npmjs.com/package/@nalvietnam/avatar-cli")
|
|
131
|
-
].join("\n");
|
|
132
|
-
process.stdout.write(
|
|
133
|
-
`${boxen(nextSteps, { padding: 1, borderStyle: "round", borderColor: "magenta" })}
|
|
134
|
-
`
|
|
135
|
-
);
|
|
136
|
-
}
|
|
137
|
-
export {
|
|
138
|
-
printWelcomeScreen
|
|
139
|
-
};
|
|
2
|
+
import A from"boxen";import p from"chalk";var m=[" \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 ","\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557","\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D","\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u255A\u2588\u2588\u2557 \u2588\u2588\u2554\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557","\u2588\u2588\u2551 \u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551","\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D"],d=[[217,79,30],[200,70,80],[170,70,140],[125,88,217]];function u(n,r,o){return Math.round(n+(r-n)*o)}function b(n){let o=Math.max(0,Math.min(1,n))*(d.length-1),t=Math.floor(o),s=Math.min(d.length-1,t+1),i=o-t,c=d[t],l=d[s];return[u(c[0],l[0],i),u(c[1],l[1],i),u(c[2],l[2],i)]}function y(n){if(!((process.stdout.isTTY??!1)&&p.level>0))return[...m,...n?.tagline?["",n.tagline]:[]].join(`
|
|
3
|
+
`);let t=m.map((s,i)=>{let c=m.length===1?0:i/(m.length-1),[l,v,$]=b(c);return p.rgb(l,v,$).bold(s)});return n?.tagline&&(t.push(""),t.push(p.dim(n.tagline))),t.join(`
|
|
4
|
+
`)}function g(n){process.stdout.write(`
|
|
5
|
+
${y(n)}
|
|
6
|
+
|
|
7
|
+
`)}import{readFileSync as w}from"fs";import{dirname as S,resolve as k}from"path";import{fileURLToPath as x}from"url";var a=null;function h(){if(a!==null)return a;let n=S(x(import.meta.url));for(let r=0;r<5;r++){let o=k(n,...Array(r).fill(".."),"package.json");try{let t=w(o,"utf8"),s=JSON.parse(t);if(s.name==="@nalvietnam/avatar-cli"&&typeof s.version=="string")return a=s.version,a}catch{}}return a="unknown",a}import e from"chalk";import O from"ora";var f=[{name:"login",desc:"\u0110\u0103ng nh\u1EADp Google SSO (@nal.vn)"},{name:"init",desc:"Kh\u1EDFi t\u1EA1o Avatar \u2014 3 flow t\u1EF1 nh\u1EADn di\u1EC7n"},{name:"sync",desc:"Pull team-ai-pack m\u1EDBi nh\u1EA5t"},{name:"scan",desc:"Project scanner + knowledge proposal"},{name:"review",desc:"Duy\u1EC7t pending proposals"},{name:"status",desc:"Snapshot project, pack, pending, backup"},{name:"doctor",desc:"Ch\u1EA9n \u0111o\xE1n c\xE0i \u0111\u1EB7t Avatar"},{name:"restore",desc:"Kh\xF4i ph\u1EE5c .claude/pack/ t\u1EEB backup"},{name:"commit",desc:"Commit src/ ho\u1EB7c Avatar state (client mode)"},{name:"tools",desc:"Qu\u1EA3n l\xFD system tools + MCP servers"},{name:"secrets",desc:"Qu\u1EA3n l\xFD secrets trong OS keychain"},{name:"uninstall",desc:"G\u1EE1 Avatar kh\u1ECFi project + backup"},{name:"help",desc:"Hi\u1EC3n th\u1ECB help cho t\u1EEBng l\u1EC7nh"}];function V(){g({tagline:`v${h()} \xB7 AI harness CLI for NAL Vietnam`});let n=Math.max(...f.map(t=>t.name.length)),r=f.map(t=>{let s=t.name.padEnd(n);return` ${e.cyan(s)} ${e.dim(t.desc)}`}).join(`
|
|
8
|
+
`),o=["",e.bold("13 l\u1EC7nh s\u1EB5n s\xE0ng:"),"",r,"",e.bold("Next steps:"),"",` ${e.green("1.")} ${e.cyan("avatar login")} \u0110\u0103ng nh\u1EADp SSO`,` ${e.green("2.")} ${e.cyan("avatar init")} Kh\u1EDFi t\u1EA1o d\u1EF1 \xE1n`,` ${e.green("?.")} ${e.cyan("avatar <command> --help")} Xem chi ti\u1EBFt l\u1EC7nh`,"",e.dim("Docs: https://www.npmjs.com/package/@nalvietnam/avatar-cli")].join(`
|
|
9
|
+
`);process.stdout.write(`${A(o,{padding:1,borderStyle:"round",borderColor:"magenta"})}
|
|
10
|
+
`)}export{V as printWelcomeScreen};
|
|
140
11
|
//# sourceMappingURL=print-welcome-screen.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/lib/print-welcome-screen.ts","../../src/lib/avatar-ascii-banner.ts","../../src/lib/read-cli-version-from-package-json.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 { readCliVersion } from \"./read-cli-version-from-package-json.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\nexport function printWelcomeScreen(): void {\n printAvatarBanner({ tagline: `v${readCliVersion()} · 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 // Non-null safe: lo ∈ [0, len-1] qua Math.floor + clamp, hi ∈ [0, len-1] qua Math.min.\n // TS strict mode không narrow tuple index → assert.\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","// Single source of truth cho CLI version — đọc runtime từ package.json.\n//\n// Lý do: trước đây version hardcode ở nhiều file (index.ts, status.ts,\n// init-scaffold-variable-builders.ts, print-welcome-screen.ts) → drift bug khi\n// bump version chỉ ở 1 chỗ, các chỗ khác in version cũ.\n//\n// Layout runtime:\n// bin/avatar.js → dist/index.js (bundled) → package.json ở \"../package.json\"\n// từ dist/. Dev mode (vitest) → src/lib/*.ts → package.json ở \"../../package.json\".\n// Caller dùng helper này không cần biết path, chỉ gọi readCliVersion().\n\nimport { readFileSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n// Cache để tránh đọc disk mỗi lần gọi (CLI có thể gọi nhiều lần trong 1 process).\nlet cachedVersion: string | null = null;\n\nexport function readCliVersion(): string {\n if (cachedVersion !== null) return cachedVersion;\n\n // Tìm package.json bằng cách đi ngược lên từ vị trí file này.\n // Production (bundled): dist/index.js → ../package.json\n // Dev (vitest): src/lib/this-file.ts → ../../package.json\n // Walk up tối đa 5 level để robust với cả 2 layout:\n // - Bundled (production): dist/index.js → ../package.json (i=1)\n // - Dev (vitest/tsx): src/lib/*.ts → ../../package.json (i=2)\n // i=0 (cùng dir) cover trường hợp file move ngang sang root tương lai.\n const here = dirname(fileURLToPath(import.meta.url));\n for (let i = 0; i < 5; i++) {\n const candidate = resolve(here, ...Array(i).fill(\"..\"), \"package.json\");\n try {\n const raw = readFileSync(candidate, \"utf8\");\n const pkg = JSON.parse(raw) as { name?: string; version?: string };\n // Verify đúng package (tránh nhặt nhầm package.json của repo cha).\n if (pkg.name === \"@nalvietnam/avatar-cli\" && typeof pkg.version === \"string\") {\n cachedVersion = pkg.version;\n return cachedVersion;\n }\n } catch {\n // File không tồn tại hoặc JSON invalid → thử level tiếp theo.\n }\n }\n\n // Fallback: nếu không tìm thấy (case edge — symlink lạ, sandbox), trả \"unknown\"\n // thay vì throw. Status command vẫn hoạt động, chỉ in \"unknown\" thay vì version.\n cachedVersion = \"unknown\";\n return cachedVersion;\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/read-cli-version-from-package-json.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 { readCliVersion } from \"./read-cli-version-from-package-json.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\nexport function printWelcomeScreen(): void {\n printAvatarBanner({ tagline: `v${readCliVersion()} · 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 // Non-null safe: lo ∈ [0, len-1] qua Math.floor + clamp, hi ∈ [0, len-1] qua Math.min.\n // TS strict mode không narrow tuple index → assert.\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","// Single source of truth cho CLI version — đọc runtime từ package.json.\n//\n// Lý do: trước đây version hardcode ở nhiều file (index.ts, status.ts,\n// init-scaffold-variable-builders.ts, print-welcome-screen.ts) → drift bug khi\n// bump version chỉ ở 1 chỗ, các chỗ khác in version cũ.\n//\n// Layout runtime:\n// bin/avatar.js → dist/index.js (bundled) → package.json ở \"../package.json\"\n// từ dist/. Dev mode (vitest) → src/lib/*.ts → package.json ở \"../../package.json\".\n// Caller dùng helper này không cần biết path, chỉ gọi readCliVersion().\n\nimport { readFileSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n// Cache để tránh đọc disk mỗi lần gọi (CLI có thể gọi nhiều lần trong 1 process).\nlet cachedVersion: string | null = null;\n\nexport function readCliVersion(): string {\n if (cachedVersion !== null) return cachedVersion;\n\n // Tìm package.json bằng cách đi ngược lên từ vị trí file này.\n // Production (bundled): dist/index.js → ../package.json\n // Dev (vitest): src/lib/this-file.ts → ../../package.json\n // Walk up tối đa 5 level để robust với cả 2 layout:\n // - Bundled (production): dist/index.js → ../package.json (i=1)\n // - Dev (vitest/tsx): src/lib/*.ts → ../../package.json (i=2)\n // i=0 (cùng dir) cover trường hợp file move ngang sang root tương lai.\n const here = dirname(fileURLToPath(import.meta.url));\n for (let i = 0; i < 5; i++) {\n const candidate = resolve(here, ...Array(i).fill(\"..\"), \"package.json\");\n try {\n const raw = readFileSync(candidate, \"utf8\");\n const pkg = JSON.parse(raw) as { name?: string; version?: string };\n // Verify đúng package (tránh nhặt nhầm package.json của repo cha).\n if (pkg.name === \"@nalvietnam/avatar-cli\" && typeof pkg.version === \"string\") {\n cachedVersion = pkg.version;\n return cachedVersion;\n }\n } catch {\n // File không tồn tại hoặc JSON invalid → thử level tiếp theo.\n }\n }\n\n // Fallback: nếu không tìm thấy (case edge — symlink lạ, sandbox), trả \"unknown\"\n // thay vì throw. Status command vẫn hoạt động, chỉ in \"unknown\" thay vì version.\n cachedVersion = \"unknown\";\n return cachedVersion;\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":["boxen","chalk","BANNER_LINES","GRADIENT_STOPS","lerpChannel","a","b","t","gradientAt","scaled","lo","hi","localT","renderAvatarBanner","opts","colored","line","idx","r","g","printAvatarBanner","readFileSync","dirname","resolve","fileURLToPath","cachedVersion","readCliVersion","here","i","candidate","raw","pkg","chalk","ora","COMMANDS","printWelcomeScreen","printAvatarBanner","readCliVersion","maxNameWidth","c","commandLines","padded","chalk","nextSteps","boxen"]}
|