@nalvietnam/avatar-cli 1.9.0 → 1.11.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 +208 -76
- package/dist/index.js.map +1 -1
- package/dist/lib/print-welcome-screen.js +37 -2
- package/dist/lib/print-welcome-screen.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2311,7 +2311,7 @@ function registerGitnexusCommand(program2) {
|
|
|
2311
2311
|
}
|
|
2312
2312
|
|
|
2313
2313
|
// src/commands/init.ts
|
|
2314
|
-
import { basename, join as
|
|
2314
|
+
import { basename, join as join25, relative as relative3, resolve as resolve2 } from "path";
|
|
2315
2315
|
import { confirm as confirm5, input as input5, select as select9 } from "@inquirer/prompts";
|
|
2316
2316
|
import boxen6 from "boxen";
|
|
2317
2317
|
|
|
@@ -2482,7 +2482,8 @@ var TeamPackAccessAbortedError = class extends Error {
|
|
|
2482
2482
|
this.name = "TeamPackAccessAbortedError";
|
|
2483
2483
|
}
|
|
2484
2484
|
};
|
|
2485
|
-
|
|
2485
|
+
var DEFAULT_PACK_BRANCH = "main";
|
|
2486
|
+
async function addTeamPackSubmodule(projectRoot, tag, ssoEmail, latest = false) {
|
|
2486
2487
|
const url = resolveTeamPackRepoUrl();
|
|
2487
2488
|
const repoSlug = parseRepoSlugFromGitUrl(url);
|
|
2488
2489
|
if (repoSlug) {
|
|
@@ -2508,12 +2509,17 @@ async function addTeamPackSubmodule(projectRoot, tag, ssoEmail) {
|
|
|
2508
2509
|
}
|
|
2509
2510
|
throw err;
|
|
2510
2511
|
}
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2512
|
+
if (tag) {
|
|
2513
|
+
await checkoutTagInSubmodule(TEAM_PACK_RELATIVE_PATH, tag, projectRoot);
|
|
2514
|
+
return { pinnedTag: tag };
|
|
2515
|
+
}
|
|
2516
|
+
if (latest) {
|
|
2517
|
+
await checkoutBranchHeadInSubmodule(TEAM_PACK_RELATIVE_PATH, DEFAULT_PACK_BRANCH, projectRoot);
|
|
2518
|
+
return { pinnedTag: `${DEFAULT_PACK_BRANCH} (HEAD)` };
|
|
2516
2519
|
}
|
|
2520
|
+
const submoduleDir = join16(projectRoot, TEAM_PACK_RELATIVE_PATH);
|
|
2521
|
+
const allTags = await listTags(submoduleDir);
|
|
2522
|
+
const target = pickLatestStableSemVerTag(allTags);
|
|
2517
2523
|
if (target) {
|
|
2518
2524
|
await checkoutTagInSubmodule(TEAM_PACK_RELATIVE_PATH, target, projectRoot);
|
|
2519
2525
|
}
|
|
@@ -2573,10 +2579,10 @@ async function handleSshPermissionError() {
|
|
|
2573
2579
|
]
|
|
2574
2580
|
});
|
|
2575
2581
|
}
|
|
2576
|
-
async function addTeamPackSubmoduleWithRetryOnNetworkFail(projectRoot, tag, ssoEmail) {
|
|
2582
|
+
async function addTeamPackSubmoduleWithRetryOnNetworkFail(projectRoot, tag, ssoEmail, latest = false) {
|
|
2577
2583
|
while (true) {
|
|
2578
2584
|
try {
|
|
2579
|
-
const result = await addTeamPackSubmodule(projectRoot, tag, ssoEmail);
|
|
2585
|
+
const result = await addTeamPackSubmodule(projectRoot, tag, ssoEmail, latest);
|
|
2580
2586
|
return { pinnedTag: result.pinnedTag, skipped: false };
|
|
2581
2587
|
} catch (err) {
|
|
2582
2588
|
if (err instanceof TeamPackAccessAbortedError) throw err;
|
|
@@ -3741,8 +3747,43 @@ async function findAlternativeWorkspaceName(parent, desiredName, maxAttempts = 1
|
|
|
3741
3747
|
return null;
|
|
3742
3748
|
}
|
|
3743
3749
|
|
|
3750
|
+
// src/lib/read-cli-version-from-package-json.ts
|
|
3751
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
3752
|
+
import { dirname as dirname6, join as join24, resolve } from "path";
|
|
3753
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
3754
|
+
var cachedVersion = null;
|
|
3755
|
+
function readCliVersion() {
|
|
3756
|
+
if (cachedVersion !== null) return cachedVersion;
|
|
3757
|
+
const here = dirname6(fileURLToPath3(import.meta.url));
|
|
3758
|
+
for (let i = 0; i < 5; i++) {
|
|
3759
|
+
const candidate = resolve(here, ...Array(i).fill(".."), "package.json");
|
|
3760
|
+
try {
|
|
3761
|
+
const raw = readFileSync5(candidate, "utf8");
|
|
3762
|
+
const pkg = JSON.parse(raw);
|
|
3763
|
+
if (pkg.name === "@nalvietnam/avatar-cli" && typeof pkg.version === "string") {
|
|
3764
|
+
cachedVersion = pkg.version;
|
|
3765
|
+
return cachedVersion;
|
|
3766
|
+
}
|
|
3767
|
+
} catch {
|
|
3768
|
+
}
|
|
3769
|
+
if (i === 0) {
|
|
3770
|
+
const sibling = join24(here, "..", "package.json");
|
|
3771
|
+
try {
|
|
3772
|
+
const raw = readFileSync5(sibling, "utf8");
|
|
3773
|
+
const pkg = JSON.parse(raw);
|
|
3774
|
+
if (pkg.name === "@nalvietnam/avatar-cli" && typeof pkg.version === "string") {
|
|
3775
|
+
cachedVersion = pkg.version;
|
|
3776
|
+
return cachedVersion;
|
|
3777
|
+
}
|
|
3778
|
+
} catch {
|
|
3779
|
+
}
|
|
3780
|
+
}
|
|
3781
|
+
}
|
|
3782
|
+
cachedVersion = "unknown";
|
|
3783
|
+
return cachedVersion;
|
|
3784
|
+
}
|
|
3785
|
+
|
|
3744
3786
|
// src/commands/init-scaffold-variable-builders.ts
|
|
3745
|
-
var AVATAR_CLI_VERSION = "1.0.1";
|
|
3746
3787
|
function inferWorkspaceName(repoUrl) {
|
|
3747
3788
|
const trimmed = repoUrl.trim().replace(/\/+$/, "");
|
|
3748
3789
|
const lastSegment = trimmed.split(/[/:]/).pop() ?? "";
|
|
@@ -3788,7 +3829,7 @@ function buildScaffoldVariables(args) {
|
|
|
3788
3829
|
projectName: args.projectName,
|
|
3789
3830
|
projectDescription: args.projectDescription,
|
|
3790
3831
|
teamOwner: args.teamOwner,
|
|
3791
|
-
avatarVersion:
|
|
3832
|
+
avatarVersion: readCliVersion(),
|
|
3792
3833
|
packVersion: args.packVersion,
|
|
3793
3834
|
lastScan: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3794
3835
|
mode: args.mode,
|
|
@@ -3983,7 +4024,7 @@ async function runLogin(opts) {
|
|
|
3983
4024
|
log.success(`L\u01B0u credential v\xE0o ${USER_CONFIG_PATH} (chmod 600)`);
|
|
3984
4025
|
}
|
|
3985
4026
|
function sleep(ms) {
|
|
3986
|
-
return new Promise((
|
|
4027
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
3987
4028
|
}
|
|
3988
4029
|
|
|
3989
4030
|
// src/commands/init.ts
|
|
@@ -3999,7 +4040,7 @@ function parseBootstrapStrategyOpts(opts) {
|
|
|
3999
4040
|
);
|
|
4000
4041
|
}
|
|
4001
4042
|
function registerInitCommand(program2) {
|
|
4002
|
-
program2.command("init").description("Kh\u1EDFi t\u1EA1o Avatar \u2014 3 flow t\u1EF1 nh\u1EADn di\u1EC7n (repo / folder / new)").option("--project-status <val>", "existing-remote | existing-folder | new-project").option("--folder-path <path>", "\u0110\u01B0\u1EDDng d\u1EABn folder hi\u1EC7n c\xF3 (flow existing-folder)").option("--create-remote", "Force t\u1EA1o remote qua gh (flow existing-folder ho\u1EB7c new-project)").option("--repo-visibility <val>", "private (m\u1EB7c \u0111\u1ECBnh) | public").option("--repo-org <name>", "GitHub org/owner cho repo m\u1EDBi").option("--client-repo <url>", "URL git remote (flow existing-remote)").option("--workspace-name <name>", "T\xEAn workspace").option("--workspace-parent <path>", "Th\u01B0 m\u1EE5c cha t\u1EA1o workspace (m\u1EB7c \u0111\u1ECBnh . \u2014 CWD)").option("--pack-version <tag>", "Pin team-ai-pack v\xE0o tag c\u1EE5 th\u1EC3").option("--team-owner <email>", "Email team owner (b\u1ECF qua prompt)").option("--description <text>", "M\xF4 t\u1EA3 1 d\xF2ng c\u1EE7a d\u1EF1 \xE1n").option("--skip-scan", "B\u1ECF qua project-scanner sau scaffold").option("--skip-team-pack", "B\u1ECF qua submodule team-ai-pack (test mode)").option("--force", "B\u1ECF qua prompt khi workspace path \u0111\xE3 t\u1ED3n t\u1EA1i").option("--yes", "Auto-confirm t\u1EA5t c\u1EA3 prompt").option("--no-commit", "Skip commit workspace initial state (m\u1EB7c \u0111\u1ECBnh LU\xD4N commit)").option("--workspace-remote", "T\u1EA1o GitHub remote cho workspace root (default: prompt)").option("--ai-skip", "B\u1ECF qua phase AI setup (CI/test mode \u2014 ch\u1EA1y `avatar ai setup` sau)").option(
|
|
4043
|
+
program2.command("init").description("Kh\u1EDFi t\u1EA1o Avatar \u2014 3 flow t\u1EF1 nh\u1EADn di\u1EC7n (repo / folder / new)").option("--project-status <val>", "existing-remote | existing-folder | new-project").option("--folder-path <path>", "\u0110\u01B0\u1EDDng d\u1EABn folder hi\u1EC7n c\xF3 (flow existing-folder)").option("--create-remote", "Force t\u1EA1o remote qua gh (flow existing-folder ho\u1EB7c new-project)").option("--repo-visibility <val>", "private (m\u1EB7c \u0111\u1ECBnh) | public").option("--repo-org <name>", "GitHub org/owner cho repo m\u1EDBi").option("--client-repo <url>", "URL git remote (flow existing-remote)").option("--workspace-name <name>", "T\xEAn workspace").option("--workspace-parent <path>", "Th\u01B0 m\u1EE5c cha t\u1EA1o workspace (m\u1EB7c \u0111\u1ECBnh . \u2014 CWD)").option("--pack-version <tag>", "Pin team-ai-pack v\xE0o tag c\u1EE5 th\u1EC3").option("--latest", "Pull HEAD c\u1EE7a team-ai-pack main branch (b\u1ECF qua tag SemVer, bleeding-edge)").option("--team-owner <email>", "Email team owner (b\u1ECF qua prompt)").option("--description <text>", "M\xF4 t\u1EA3 1 d\xF2ng c\u1EE7a d\u1EF1 \xE1n").option("--skip-scan", "B\u1ECF qua project-scanner sau scaffold").option("--skip-team-pack", "B\u1ECF qua submodule team-ai-pack (test mode)").option("--force", "B\u1ECF qua prompt khi workspace path \u0111\xE3 t\u1ED3n t\u1EA1i").option("--yes", "Auto-confirm t\u1EA5t c\u1EA3 prompt").option("--no-commit", "Skip commit workspace initial state (m\u1EB7c \u0111\u1ECBnh LU\xD4N commit)").option("--workspace-remote", "T\u1EA1o GitHub remote cho workspace root (default: prompt)").option("--ai-skip", "B\u1ECF qua phase AI setup (CI/test mode \u2014 ch\u1EA1y `avatar ai setup` sau)").option(
|
|
4003
4044
|
"--gitnexus-skip",
|
|
4004
4045
|
"B\u1ECF qua phase GitNexus setup (M10 \u2014 ch\u1EA1y `avatar gitnexus install` sau)"
|
|
4005
4046
|
).option(
|
|
@@ -4091,7 +4132,7 @@ async function runInitFromExistingRemote(opts, ownerEmail) {
|
|
|
4091
4132
|
const teamOwner = opts.teamOwner ?? await promptTeamOwner(ownerEmail);
|
|
4092
4133
|
const inferredName = inferWorkspaceName(remoteUrl);
|
|
4093
4134
|
const workspaceName = opts.workspaceName ?? await input5({ message: "T\xEAn workspace:", default: inferredName });
|
|
4094
|
-
const workspaceParent =
|
|
4135
|
+
const workspaceParent = resolve2(opts.workspaceParent ?? ".");
|
|
4095
4136
|
const workspacePath = await resolveWorkspacePath(workspaceParent, workspaceName, opts.force);
|
|
4096
4137
|
await scaffoldWorkspaceWithSrcSubmodule({
|
|
4097
4138
|
workspacePath,
|
|
@@ -4101,6 +4142,7 @@ async function runInitFromExistingRemote(opts, ownerEmail) {
|
|
|
4101
4142
|
skipTeamPack: opts.skipTeamPack,
|
|
4102
4143
|
description: opts.description ?? `Avatar workspace cho ${remoteUrl}`,
|
|
4103
4144
|
packVersion: opts.packVersion,
|
|
4145
|
+
packLatest: opts.latest,
|
|
4104
4146
|
autoYes: opts.yes,
|
|
4105
4147
|
skipCommit: opts.commit === false,
|
|
4106
4148
|
createWorkspaceRemote: opts.workspaceRemote,
|
|
@@ -4113,7 +4155,7 @@ async function runInitFromExistingRemote(opts, ownerEmail) {
|
|
|
4113
4155
|
});
|
|
4114
4156
|
}
|
|
4115
4157
|
async function runInitFromExistingFolder(opts, ownerEmail) {
|
|
4116
|
-
const folderPath =
|
|
4158
|
+
const folderPath = resolve2(
|
|
4117
4159
|
opts.folderPath ?? await input5({
|
|
4118
4160
|
message: "\u0110\u01B0\u1EDDng d\u1EABn folder hi\u1EC7n c\xF3:",
|
|
4119
4161
|
validate: (v) => v.length > 0 ? true : "Path b\u1EAFt bu\u1ED9c"
|
|
@@ -4127,7 +4169,7 @@ async function runInitFromExistingFolder(opts, ownerEmail) {
|
|
|
4127
4169
|
const teamOwner = opts.teamOwner ?? await promptTeamOwner(ownerEmail);
|
|
4128
4170
|
const inferredName = opts.workspaceName ?? `${basename(folderPath)}-avatar-workspace`;
|
|
4129
4171
|
const workspaceName = opts.workspaceName ?? await input5({ message: "T\xEAn workspace:", default: inferredName });
|
|
4130
|
-
const workspaceParent =
|
|
4172
|
+
const workspaceParent = resolve2(opts.workspaceParent ?? ".");
|
|
4131
4173
|
const workspacePath = await resolveWorkspacePath(workspaceParent, workspaceName, opts.force);
|
|
4132
4174
|
await scaffoldWorkspaceWithSrcSubmodule({
|
|
4133
4175
|
workspacePath,
|
|
@@ -4138,6 +4180,7 @@ async function runInitFromExistingFolder(opts, ownerEmail) {
|
|
|
4138
4180
|
skipTeamPack: opts.skipTeamPack,
|
|
4139
4181
|
description: opts.description ?? `Avatar workspace cho folder ${folderPath}`,
|
|
4140
4182
|
packVersion: opts.packVersion,
|
|
4183
|
+
packLatest: opts.latest,
|
|
4141
4184
|
autoYes: opts.yes,
|
|
4142
4185
|
skipCommit: opts.commit === false,
|
|
4143
4186
|
createWorkspaceRemote: opts.workspaceRemote,
|
|
@@ -4163,9 +4206,9 @@ async function runInitFromScratch(opts, ownerEmail) {
|
|
|
4163
4206
|
]
|
|
4164
4207
|
});
|
|
4165
4208
|
const teamOwner = opts.teamOwner ?? await promptTeamOwner(ownerEmail);
|
|
4166
|
-
const workspaceParent =
|
|
4209
|
+
const workspaceParent = resolve2(opts.workspaceParent ?? ".");
|
|
4167
4210
|
const workspacePath = await resolveWorkspacePath(workspaceParent, projectName, opts.force);
|
|
4168
|
-
const srcPath =
|
|
4211
|
+
const srcPath = join25(workspacePath, "src");
|
|
4169
4212
|
await ensureDir(workspacePath);
|
|
4170
4213
|
await ensureDir(srcPath);
|
|
4171
4214
|
await safeBootstrapGitInFolder(srcPath, { autoYes: true });
|
|
@@ -4187,7 +4230,9 @@ async function runInitFromScratch(opts, ownerEmail) {
|
|
|
4187
4230
|
const result = await addTeamPackSubmoduleWithRetryOnNetworkFail(
|
|
4188
4231
|
workspacePath,
|
|
4189
4232
|
opts.packVersion,
|
|
4190
|
-
ownerEmail
|
|
4233
|
+
ownerEmail,
|
|
4234
|
+
opts.latest === true && !opts.packVersion
|
|
4235
|
+
// v1.10.0: latest mode khi flag set + không có explicit tag
|
|
4191
4236
|
);
|
|
4192
4237
|
pinnedTag = result.pinnedTag ?? "HEAD";
|
|
4193
4238
|
sp.succeed(`Pin team-ai-pack v\xE0o ${pinnedTag}`);
|
|
@@ -4263,7 +4308,9 @@ async function scaffoldWorkspaceWithSrcSubmodule(args) {
|
|
|
4263
4308
|
const result = await addTeamPackSubmoduleWithRetryOnNetworkFail(
|
|
4264
4309
|
args.workspacePath,
|
|
4265
4310
|
args.packVersion,
|
|
4266
|
-
args.ssoEmail
|
|
4311
|
+
args.ssoEmail,
|
|
4312
|
+
args.packLatest === true && !args.packVersion
|
|
4313
|
+
// v1.10.0
|
|
4267
4314
|
);
|
|
4268
4315
|
pinnedTag = result.pinnedTag ?? "HEAD";
|
|
4269
4316
|
sp.succeed(`Pin team-ai-pack v\xE0o ${pinnedTag}`);
|
|
@@ -4303,10 +4350,10 @@ async function finalizeWorkspaceScaffold(args) {
|
|
|
4303
4350
|
await writeRootClaudeMd(args.workspacePath, vars);
|
|
4304
4351
|
await writeProjectSettings(args.workspacePath, vars);
|
|
4305
4352
|
await appendGitignoreEntries(args.workspacePath);
|
|
4306
|
-
await ensureDir(
|
|
4307
|
-
await ensureDir(
|
|
4308
|
-
await installGitHook(
|
|
4309
|
-
await installGitHook(
|
|
4353
|
+
await ensureDir(join25(args.workspacePath, "notes"));
|
|
4354
|
+
await ensureDir(join25(args.workspacePath, "scripts"));
|
|
4355
|
+
await installGitHook(join25(args.workspacePath, ".git"), "post-merge");
|
|
4356
|
+
await installGitHook(join25(args.workspacePath, ".git", "modules", "src"), "pre-push");
|
|
4310
4357
|
log.success("C\xE0i post-merge (workspace) + pre-push (src/)");
|
|
4311
4358
|
await autoSyncPackOnInit(args.workspacePath);
|
|
4312
4359
|
await appendAuditEntry("init", `flow=${args.flow},workspace=${args.workspaceName}`);
|
|
@@ -4344,12 +4391,12 @@ async function finalizeWorkspaceScaffold(args) {
|
|
|
4344
4391
|
await printInitSuccessBox(args.workspacePath, args.flow, aiResult, gitnexusResult);
|
|
4345
4392
|
}
|
|
4346
4393
|
async function autoSyncPackOnInit(workspacePath) {
|
|
4347
|
-
const packDir =
|
|
4394
|
+
const packDir = join25(workspacePath, TEAM_PACK_RELATIVE_PATH);
|
|
4348
4395
|
if (!await pathExists(packDir)) {
|
|
4349
4396
|
log.dim("Pack submodule kh\xF4ng t\u1ED3n t\u1EA1i (skip auto-sync). C\xF3 th\u1EC3 ch\u1EA1y `avatar sync` sau.");
|
|
4350
4397
|
return;
|
|
4351
4398
|
}
|
|
4352
|
-
const claudeDir =
|
|
4399
|
+
const claudeDir = join25(workspacePath, ".claude");
|
|
4353
4400
|
log.info("Auto-sync pack content v\xE0o .claude/ (symlinks + settings merge)...");
|
|
4354
4401
|
try {
|
|
4355
4402
|
const results = await syncAllMountDirs(packDir, claudeDir, false);
|
|
@@ -4463,7 +4510,7 @@ async function maybeCreateWorkspaceRemote(args) {
|
|
|
4463
4510
|
}
|
|
4464
4511
|
}
|
|
4465
4512
|
async function resolveWorkspacePath(parent, desiredName, force) {
|
|
4466
|
-
const desired =
|
|
4513
|
+
const desired = join25(parent, desiredName);
|
|
4467
4514
|
if (await isEmptyOrMissing(desired)) return desired;
|
|
4468
4515
|
log.warn(`Workspace path "${desired}" \u0111\xE3 c\xF3 n\u1ED9i dung.`);
|
|
4469
4516
|
while (true) {
|
|
@@ -4494,7 +4541,7 @@ async function resolveWorkspacePath(parent, desiredName, force) {
|
|
|
4494
4541
|
message: "T\xEAn workspace m\u1EDBi:",
|
|
4495
4542
|
validate: (v) => v.trim().length > 0 ? true : "T\xEAn kh\xF4ng \u0111\u01B0\u1EE3c r\u1ED7ng"
|
|
4496
4543
|
});
|
|
4497
|
-
const newPath =
|
|
4544
|
+
const newPath = join25(parent, newName.trim());
|
|
4498
4545
|
if (await isEmptyOrMissing(newPath)) return newPath;
|
|
4499
4546
|
log.warn(`"${newPath}" c\u0169ng \u0111\xE3 c\xF3 n\u1ED9i dung. Th\u1EED t\xEAn kh\xE1c.`);
|
|
4500
4547
|
}
|
|
@@ -4551,7 +4598,7 @@ async function printInitSuccessBox(rootPath, flow, aiResult = null, gitnexusResu
|
|
|
4551
4598
|
];
|
|
4552
4599
|
process.stdout.write(`${boxen6(lines.join("\n"), { padding: 1, borderStyle: "round" })}
|
|
4553
4600
|
`);
|
|
4554
|
-
const packDir =
|
|
4601
|
+
const packDir = join25(rootPath, TEAM_PACK_RELATIVE_PATH);
|
|
4555
4602
|
if (await pathExists(packDir)) {
|
|
4556
4603
|
process.stdout.write(`
|
|
4557
4604
|
${formatPackCommandsCheatsheetBox()}
|
|
@@ -4580,6 +4627,91 @@ function registerMcpRunCommand(program2) {
|
|
|
4580
4627
|
program2.command("mcp-run <tool-id>", { hidden: true }).description("[internal] Spawn MCP v\u1EDBi secrets injected (M09)").action(notImplementedYet("mcp-run", "Milestone 09"));
|
|
4581
4628
|
}
|
|
4582
4629
|
|
|
4630
|
+
// src/commands/pack-status-and-version-check.ts
|
|
4631
|
+
import { join as join26 } from "path";
|
|
4632
|
+
import boxen7 from "boxen";
|
|
4633
|
+
var PACK_RELATIVE_PATH = ".claude/pack";
|
|
4634
|
+
function registerPackCommand(program2) {
|
|
4635
|
+
const pack = program2.command("pack").description("Qu\u1EA3n l\xFD team-ai-pack submodule (status, ...)");
|
|
4636
|
+
pack.command("status").description("Hi\u1EC3n th\u1ECB tag pack \u0111ang pin + tag m\u1EDBi nh\u1EA5t t\u1EEB remote").option("--json", "Output JSON cho script").option("--no-fetch", "B\u1ECF qua fetch remote (ch\u1EC9 \u0111\u1ECDc local tags)").action(async (opts) => {
|
|
4637
|
+
try {
|
|
4638
|
+
const snap = await gatherPackStatus(process.cwd(), opts.fetch !== false);
|
|
4639
|
+
if (opts.json) {
|
|
4640
|
+
process.stdout.write(`${JSON.stringify(snap, null, 2)}
|
|
4641
|
+
`);
|
|
4642
|
+
} else {
|
|
4643
|
+
renderPackStatus(snap);
|
|
4644
|
+
}
|
|
4645
|
+
} catch (err) {
|
|
4646
|
+
log.error(err instanceof Error ? err.message : String(err));
|
|
4647
|
+
process.exit(1);
|
|
4648
|
+
}
|
|
4649
|
+
});
|
|
4650
|
+
}
|
|
4651
|
+
async function gatherPackStatus(cwd, doFetch) {
|
|
4652
|
+
const packDir = join26(cwd, PACK_RELATIVE_PATH);
|
|
4653
|
+
if (!await pathExists(packDir) || !await isGitRepo(packDir)) {
|
|
4654
|
+
return {
|
|
4655
|
+
installed: false,
|
|
4656
|
+
currentTag: null,
|
|
4657
|
+
latestTag: null,
|
|
4658
|
+
upToDate: null,
|
|
4659
|
+
fetched: false
|
|
4660
|
+
};
|
|
4661
|
+
}
|
|
4662
|
+
const currentTag = await readPinnedPackVersion(cwd).catch(() => null);
|
|
4663
|
+
let fetched = false;
|
|
4664
|
+
if (doFetch) {
|
|
4665
|
+
try {
|
|
4666
|
+
await git(packDir).fetch(["--tags", "origin"]);
|
|
4667
|
+
fetched = true;
|
|
4668
|
+
} catch {
|
|
4669
|
+
fetched = false;
|
|
4670
|
+
}
|
|
4671
|
+
}
|
|
4672
|
+
const allTags = await listTags(packDir).catch(() => []);
|
|
4673
|
+
const latestTag = pickLatestStableSemVerTag(allTags);
|
|
4674
|
+
const upToDate = currentTag && latestTag ? currentTag === latestTag : null;
|
|
4675
|
+
return {
|
|
4676
|
+
installed: true,
|
|
4677
|
+
currentTag,
|
|
4678
|
+
latestTag,
|
|
4679
|
+
upToDate,
|
|
4680
|
+
fetched
|
|
4681
|
+
};
|
|
4682
|
+
}
|
|
4683
|
+
function renderPackStatus(s) {
|
|
4684
|
+
if (!s.installed) {
|
|
4685
|
+
const lines2 = [
|
|
4686
|
+
`${chalk.bold("team-ai-pack")} \xB7 ${chalk.yellow("ch\u01B0a c\xE0i")}`,
|
|
4687
|
+
"\u2500".repeat(48),
|
|
4688
|
+
chalk.dim("Ch\u1EA1y `avatar init` ho\u1EB7c `avatar sync` \u0111\u1EC3 c\xE0i pack.")
|
|
4689
|
+
];
|
|
4690
|
+
process.stdout.write(`${boxen7(lines2.join("\n"), { padding: 1, borderStyle: "round" })}
|
|
4691
|
+
`);
|
|
4692
|
+
return;
|
|
4693
|
+
}
|
|
4694
|
+
const current = s.currentTag ?? chalk.yellow("(unknown)");
|
|
4695
|
+
const latest = s.latestTag ?? chalk.dim(s.fetched ? "(no tags)" : "(kh\xF4ng fetch)");
|
|
4696
|
+
let verdict;
|
|
4697
|
+
if (s.upToDate === true) {
|
|
4698
|
+
verdict = chalk.green("\u2713 \u0110ang d\xF9ng tag m\u1EDBi nh\u1EA5t");
|
|
4699
|
+
} else if (s.upToDate === false) {
|
|
4700
|
+
verdict = `${chalk.yellow("\u26A0 C\xF3 version m\u1EDBi")} ${chalk.dim("\u2192 avatar sync")}`;
|
|
4701
|
+
} else {
|
|
4702
|
+
verdict = chalk.dim("(kh\xF4ng so s\xE1nh \u0111\u01B0\u1EE3c)");
|
|
4703
|
+
}
|
|
4704
|
+
const lines = [
|
|
4705
|
+
`${chalk.bold("team-ai-pack status")}`,
|
|
4706
|
+
"\u2500".repeat(48),
|
|
4707
|
+
`${chalk.dim("Tag hi\u1EC7n t\u1EA1i:")} ${current}`,
|
|
4708
|
+
`${chalk.dim("Tag m\u1EDBi nh\u1EA5t:")} ${latest}`,
|
|
4709
|
+
`${chalk.dim("Tr\u1EA1ng th\xE1i:")} ${verdict}`
|
|
4710
|
+
];
|
|
4711
|
+
process.stdout.write(`${boxen7(lines.join("\n"), { padding: 1, borderStyle: "round" })}
|
|
4712
|
+
`);
|
|
4713
|
+
}
|
|
4714
|
+
|
|
4583
4715
|
// src/commands/restore.ts
|
|
4584
4716
|
function registerRestoreCommand(program2) {
|
|
4585
4717
|
program2.command("restore").description("Kh\xF4i ph\u1EE5c .claude/pack/ t\u1EEB backup (M08)").option("--backup <name>", "T\xEAn backup folder trong .claude/_backup/").option("--list", "Li\u1EC7t k\xEA c\xE1c backup hi\u1EC7n c\xF3").action(notImplementedYet("restore", "Milestone 08"));
|
|
@@ -4607,22 +4739,21 @@ function registerSecretsCommand(program2) {
|
|
|
4607
4739
|
|
|
4608
4740
|
// src/commands/status.ts
|
|
4609
4741
|
import { promises as fs13 } from "fs";
|
|
4610
|
-
import { join as
|
|
4611
|
-
import
|
|
4742
|
+
import { join as join28 } from "path";
|
|
4743
|
+
import boxen8 from "boxen";
|
|
4612
4744
|
|
|
4613
4745
|
// src/lib/pack-backup-manager.ts
|
|
4614
4746
|
import { promises as fs12 } from "fs";
|
|
4615
|
-
import { join as
|
|
4747
|
+
import { join as join27 } from "path";
|
|
4616
4748
|
var BACKUP_DIR_NAME = "_backup";
|
|
4617
4749
|
async function listBackups(projectRoot) {
|
|
4618
|
-
const dir =
|
|
4750
|
+
const dir = join27(projectRoot, ".claude", BACKUP_DIR_NAME);
|
|
4619
4751
|
if (!await pathExists(dir)) return [];
|
|
4620
4752
|
const entries = await fs12.readdir(dir, { withFileTypes: true });
|
|
4621
4753
|
return entries.filter((e) => e.isDirectory()).map((e) => e.name).sort().reverse();
|
|
4622
4754
|
}
|
|
4623
4755
|
|
|
4624
4756
|
// src/commands/status.ts
|
|
4625
|
-
var AVATAR_CLI_VERSION2 = "1.0.1";
|
|
4626
4757
|
function registerStatusCommand(program2) {
|
|
4627
4758
|
program2.command("status").description("Snapshot t\u1EE9c th\xEC: project, pack version, pending, backup").option("--json", "Output JSON cho script").action(async (opts) => {
|
|
4628
4759
|
try {
|
|
@@ -4641,12 +4772,12 @@ function registerStatusCommand(program2) {
|
|
|
4641
4772
|
}
|
|
4642
4773
|
async function gatherStatus(cwd) {
|
|
4643
4774
|
const projectName = cwd.split("/").filter(Boolean).pop() ?? "unknown";
|
|
4644
|
-
const claudeRoot =
|
|
4775
|
+
const claudeRoot = join28(cwd, ".claude");
|
|
4645
4776
|
const hasAvatar = await pathExists(claudeRoot);
|
|
4646
4777
|
if (!hasAvatar) {
|
|
4647
4778
|
return {
|
|
4648
4779
|
projectName,
|
|
4649
|
-
cliVersion:
|
|
4780
|
+
cliVersion: readCliVersion(),
|
|
4650
4781
|
packVersion: null,
|
|
4651
4782
|
pendingCount: 0,
|
|
4652
4783
|
backupCount: 0,
|
|
@@ -4654,14 +4785,14 @@ async function gatherStatus(cwd) {
|
|
|
4654
4785
|
hasAvatar: false
|
|
4655
4786
|
};
|
|
4656
4787
|
}
|
|
4657
|
-
const packVersion = await isGitRepo(
|
|
4658
|
-
const pendingDir =
|
|
4788
|
+
const packVersion = await isGitRepo(join28(claudeRoot, "pack")) ? await readPinnedPackVersion(cwd).catch(() => null) : null;
|
|
4789
|
+
const pendingDir = join28(claudeRoot, "_pending");
|
|
4659
4790
|
const pendingCount = await pathExists(pendingDir) ? (await fs13.readdir(pendingDir)).filter((n) => n.endsWith(".diff.md")).length : 0;
|
|
4660
4791
|
const backupCount = (await listBackups(cwd)).length;
|
|
4661
4792
|
const techStackSummary = await readTechStackFirstLine(claudeRoot);
|
|
4662
4793
|
return {
|
|
4663
4794
|
projectName,
|
|
4664
|
-
cliVersion:
|
|
4795
|
+
cliVersion: AVATAR_CLI_VERSION,
|
|
4665
4796
|
packVersion,
|
|
4666
4797
|
pendingCount,
|
|
4667
4798
|
backupCount,
|
|
@@ -4670,7 +4801,7 @@ async function gatherStatus(cwd) {
|
|
|
4670
4801
|
};
|
|
4671
4802
|
}
|
|
4672
4803
|
async function readTechStackFirstLine(claudeRoot) {
|
|
4673
|
-
const techStackPath =
|
|
4804
|
+
const techStackPath = join28(claudeRoot, "project", "tech-stack.md");
|
|
4674
4805
|
if (!await pathExists(techStackPath)) return "(no tech-stack.md)";
|
|
4675
4806
|
const content = await readText(techStackPath);
|
|
4676
4807
|
const firstNonHeaderLine = content.split("\n").find((l) => l.trim() && !l.startsWith("#") && !l.startsWith(">"));
|
|
@@ -4686,18 +4817,18 @@ function renderStatusBox(s) {
|
|
|
4686
4817
|
`${chalk.dim("Backups:")} ${s.backupCount}`,
|
|
4687
4818
|
`${chalk.dim("Tech stack:")} ${s.techStackSummary}`
|
|
4688
4819
|
];
|
|
4689
|
-
process.stdout.write(`${
|
|
4820
|
+
process.stdout.write(`${boxen8(lines.join("\n"), { padding: 1, borderStyle: "round" })}
|
|
4690
4821
|
`);
|
|
4691
4822
|
}
|
|
4692
4823
|
|
|
4693
4824
|
// src/commands/sync.ts
|
|
4694
|
-
import { join as
|
|
4825
|
+
import { join as join30 } from "path";
|
|
4695
4826
|
|
|
4696
4827
|
// src/lib/preview-team-pack-sync-changes-for-dry-run.ts
|
|
4697
|
-
import { join as
|
|
4828
|
+
import { join as join29 } from "path";
|
|
4698
4829
|
async function inspectMountDir(packDir, claudeDir, dir) {
|
|
4699
|
-
const source =
|
|
4700
|
-
const dest =
|
|
4830
|
+
const source = join29(packDir, dir);
|
|
4831
|
+
const dest = join29(claudeDir, dir);
|
|
4701
4832
|
if (!await pathExists(source)) return "source-missing";
|
|
4702
4833
|
if (!await pathExists(dest)) return "needs-creation";
|
|
4703
4834
|
const { promises: fs14 } = await import("fs");
|
|
@@ -4736,11 +4867,11 @@ async function buildSyncPreview(packDir, claudeDir, targetVersion) {
|
|
|
4736
4867
|
}
|
|
4737
4868
|
|
|
4738
4869
|
// src/commands/sync.ts
|
|
4739
|
-
var
|
|
4870
|
+
var DEFAULT_PACK_BRANCH2 = "main";
|
|
4740
4871
|
async function syncAction(opts) {
|
|
4741
4872
|
const projectRoot = process.cwd();
|
|
4742
|
-
const claudeDir =
|
|
4743
|
-
const packDir =
|
|
4873
|
+
const claudeDir = join30(projectRoot, ".claude");
|
|
4874
|
+
const packDir = join30(projectRoot, TEAM_PACK_RELATIVE_PATH);
|
|
4744
4875
|
if (!await pathExists(packDir)) {
|
|
4745
4876
|
log.error(
|
|
4746
4877
|
`team-ai-pack submodule ch\u01B0a \u0111\u01B0\u1EE3c kh\u1EDFi t\u1EA1o \u1EDF ${TEAM_PACK_RELATIVE_PATH}/.
|
|
@@ -4760,14 +4891,14 @@ async function syncAction(opts) {
|
|
|
4760
4891
|
const allTags = await listTags(packDir);
|
|
4761
4892
|
let targetVersion;
|
|
4762
4893
|
if (useLatestMode) {
|
|
4763
|
-
targetVersion = `${
|
|
4894
|
+
targetVersion = `${DEFAULT_PACK_BRANCH2} (HEAD)`;
|
|
4764
4895
|
} else {
|
|
4765
4896
|
const picked = opts.version ?? pickLatestStableSemVerTag(allTags);
|
|
4766
4897
|
if (!picked) {
|
|
4767
4898
|
log.error(
|
|
4768
4899
|
`Kh\xF4ng t\xECm th\u1EA5y stable SemVer tag (vMAJOR.MINOR.PATCH) trong team-ai-pack submodule.
|
|
4769
4900
|
Tags hi\u1EC7n c\xF3: ${allTags.length > 0 ? allTags.join(", ") : "(none)"}
|
|
4770
|
-
Pass --version <tag> r\xF5 r\xE0ng, ho\u1EB7c d\xF9ng --latest \u0111\u1EC3 pull HEAD branch ${
|
|
4901
|
+
Pass --version <tag> r\xF5 r\xE0ng, ho\u1EB7c d\xF9ng --latest \u0111\u1EC3 pull HEAD branch ${DEFAULT_PACK_BRANCH2}.`
|
|
4771
4902
|
);
|
|
4772
4903
|
process.exit(1);
|
|
4773
4904
|
return;
|
|
@@ -4797,8 +4928,8 @@ async function syncAction(opts) {
|
|
|
4797
4928
|
return;
|
|
4798
4929
|
}
|
|
4799
4930
|
if (useLatestMode) {
|
|
4800
|
-
log.info(`Pulling HEAD c\u1EE7a branch ${
|
|
4801
|
-
await checkoutBranchHeadInSubmodule(TEAM_PACK_RELATIVE_PATH,
|
|
4931
|
+
log.info(`Pulling HEAD c\u1EE7a branch ${DEFAULT_PACK_BRANCH2} (bleeding-edge mode)...`);
|
|
4932
|
+
await checkoutBranchHeadInSubmodule(TEAM_PACK_RELATIVE_PATH, DEFAULT_PACK_BRANCH2, projectRoot);
|
|
4802
4933
|
const sha = await currentCommitSha(packDir);
|
|
4803
4934
|
log.dim(` HEAD = ${sha.slice(0, 7)}`);
|
|
4804
4935
|
log.warn(
|
|
@@ -4878,32 +5009,32 @@ function registerToolsCommand(program2) {
|
|
|
4878
5009
|
// src/commands/uninstall.ts
|
|
4879
5010
|
import { relative as relative4 } from "path";
|
|
4880
5011
|
import { confirm as confirm6 } from "@inquirer/prompts";
|
|
4881
|
-
import
|
|
5012
|
+
import boxen9 from "boxen";
|
|
4882
5013
|
|
|
4883
5014
|
// src/lib/create-uninstall-backup-snapshot.ts
|
|
4884
5015
|
import { cp, mkdir, writeFile } from "fs/promises";
|
|
4885
5016
|
import { homedir as homedir4 } from "os";
|
|
4886
|
-
import { basename as basename2, join as
|
|
4887
|
-
var UNINSTALL_BACKUPS_DIR =
|
|
5017
|
+
import { basename as basename2, join as join31 } from "path";
|
|
5018
|
+
var UNINSTALL_BACKUPS_DIR = join31(homedir4(), ".avatar", "uninstall-backups");
|
|
4888
5019
|
async function createUninstallBackupSnapshot(projectRoot, artifacts, avatarVersion) {
|
|
4889
5020
|
const projectName = basename2(projectRoot);
|
|
4890
5021
|
const timestamp2 = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
4891
|
-
const backupDir =
|
|
5022
|
+
const backupDir = join31(UNINSTALL_BACKUPS_DIR, `${projectName}-${timestamp2}`);
|
|
4892
5023
|
await mkdir(backupDir, { recursive: true, mode: 448 });
|
|
4893
5024
|
if (artifacts.claudeDir) {
|
|
4894
|
-
await cp(artifacts.claudeDir,
|
|
5025
|
+
await cp(artifacts.claudeDir, join31(backupDir, ".claude"), { recursive: true });
|
|
4895
5026
|
}
|
|
4896
5027
|
if (artifacts.claudeMd) {
|
|
4897
|
-
await cp(artifacts.claudeMd,
|
|
5028
|
+
await cp(artifacts.claudeMd, join31(backupDir, "CLAUDE.md"));
|
|
4898
5029
|
}
|
|
4899
5030
|
if (artifacts.postMergeHook || artifacts.prePushHook) {
|
|
4900
|
-
const hooksBackupDir =
|
|
5031
|
+
const hooksBackupDir = join31(backupDir, "hooks");
|
|
4901
5032
|
await mkdir(hooksBackupDir, { recursive: true });
|
|
4902
5033
|
if (artifacts.postMergeHook) {
|
|
4903
|
-
await cp(artifacts.postMergeHook,
|
|
5034
|
+
await cp(artifacts.postMergeHook, join31(hooksBackupDir, "post-merge"));
|
|
4904
5035
|
}
|
|
4905
5036
|
if (artifacts.prePushHook) {
|
|
4906
|
-
await cp(artifacts.prePushHook,
|
|
5037
|
+
await cp(artifacts.prePushHook, join31(hooksBackupDir, "pre-push"));
|
|
4907
5038
|
}
|
|
4908
5039
|
}
|
|
4909
5040
|
const manifest = {
|
|
@@ -4918,27 +5049,27 @@ async function createUninstallBackupSnapshot(projectRoot, artifacts, avatarVersi
|
|
|
4918
5049
|
prePushHook: !!artifacts.prePushHook
|
|
4919
5050
|
}
|
|
4920
5051
|
};
|
|
4921
|
-
await writeFile(
|
|
5052
|
+
await writeFile(join31(backupDir, "manifest.json"), JSON.stringify(manifest, null, 2), "utf8");
|
|
4922
5053
|
return backupDir;
|
|
4923
5054
|
}
|
|
4924
5055
|
|
|
4925
5056
|
// src/lib/detect-avatar-project-artifacts.ts
|
|
4926
5057
|
import { existsSync as existsSync9 } from "fs";
|
|
4927
|
-
import { join as
|
|
5058
|
+
import { join as join32 } from "path";
|
|
4928
5059
|
function existsOrNull(path) {
|
|
4929
5060
|
return existsSync9(path) ? path : null;
|
|
4930
5061
|
}
|
|
4931
5062
|
function detectAvatarProjectArtifacts(projectRoot) {
|
|
4932
|
-
const claudeDir = existsOrNull(
|
|
4933
|
-
const claudeMd = existsOrNull(
|
|
4934
|
-
const postMergeHook = existsOrNull(
|
|
5063
|
+
const claudeDir = existsOrNull(join32(projectRoot, ".claude"));
|
|
5064
|
+
const claudeMd = existsOrNull(join32(projectRoot, "CLAUDE.md"));
|
|
5065
|
+
const postMergeHook = existsOrNull(join32(projectRoot, ".git", "hooks", "post-merge"));
|
|
4935
5066
|
const prePushHook = existsOrNull(
|
|
4936
|
-
|
|
5067
|
+
join32(projectRoot, ".git", "modules", "src", "hooks", "pre-push")
|
|
4937
5068
|
);
|
|
4938
|
-
const gitignorePath = existsOrNull(
|
|
4939
|
-
const gitmodulesPath = existsOrNull(
|
|
4940
|
-
const notesDir = existsOrNull(
|
|
4941
|
-
const scriptsDir = existsOrNull(
|
|
5069
|
+
const gitignorePath = existsOrNull(join32(projectRoot, ".gitignore"));
|
|
5070
|
+
const gitmodulesPath = existsOrNull(join32(projectRoot, ".gitmodules"));
|
|
5071
|
+
const notesDir = existsOrNull(join32(projectRoot, "notes"));
|
|
5072
|
+
const scriptsDir = existsOrNull(join32(projectRoot, "scripts"));
|
|
4942
5073
|
const hasAnyArtifact = !!(claudeDir || claudeMd || postMergeHook || prePushHook);
|
|
4943
5074
|
return {
|
|
4944
5075
|
hasAnyArtifact,
|
|
@@ -4959,11 +5090,11 @@ async function executeUninstallDeletion(artifacts, flags) {
|
|
|
4959
5090
|
if (artifacts.claudeDir) {
|
|
4960
5091
|
if (flags.keepSubmodule) {
|
|
4961
5092
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
4962
|
-
const { join:
|
|
5093
|
+
const { join: join33 } = await import("path");
|
|
4963
5094
|
const entries = await readdir2(artifacts.claudeDir);
|
|
4964
5095
|
for (const entry of entries) {
|
|
4965
5096
|
if (entry === "pack") continue;
|
|
4966
|
-
await rm(
|
|
5097
|
+
await rm(join33(artifacts.claudeDir, entry), { recursive: true, force: true });
|
|
4967
5098
|
}
|
|
4968
5099
|
} else {
|
|
4969
5100
|
await rm(artifacts.claudeDir, { recursive: true, force: true });
|
|
@@ -5032,7 +5163,7 @@ async function removeSubmoduleEntry(gitmodulesPath, submodulePath) {
|
|
|
5032
5163
|
}
|
|
5033
5164
|
|
|
5034
5165
|
// src/commands/uninstall.ts
|
|
5035
|
-
var CLI_VERSION = "1.
|
|
5166
|
+
var CLI_VERSION = "1.10.0";
|
|
5036
5167
|
function registerUninstallCommand(program2) {
|
|
5037
5168
|
program2.command("uninstall").description("G\u1EE1 Avatar kh\u1ECFi project \u2014 backup t\u1EF1 \u0111\u1ED9ng (M11)").option("--yes", "Skip confirm prompt").option("--no-backup", "Kh\xF4ng t\u1EA1o backup tr\u01B0\u1EDBc khi x\xF3a (nguy hi\u1EC3m)").option("--keep-submodule", "Gi\u1EEF submodule .claude/pack/").option("--keep-hooks", "Gi\u1EEF git hooks post-merge, pre-push").option("--dry-run", "Hi\u1EC3n th\u1ECB danh s\xE1ch s\u1EBD x\xF3a, kh\xF4ng th\u1EF1c thi").action(async (opts) => {
|
|
5038
5169
|
try {
|
|
@@ -5109,12 +5240,12 @@ function printUninstallSuccessBox(backupPath) {
|
|
|
5109
5240
|
lines.push(` ${chalk.dim("Backup:")} ${backupPath}`);
|
|
5110
5241
|
lines.push(` ${chalk.dim("Restore:")} ${chalk.cyan(`cp -r "${backupPath}"/* .`)}`);
|
|
5111
5242
|
}
|
|
5112
|
-
process.stdout.write(`${
|
|
5243
|
+
process.stdout.write(`${boxen9(lines.join("\n"), { padding: 1, borderStyle: "round" })}
|
|
5113
5244
|
`);
|
|
5114
5245
|
}
|
|
5115
5246
|
|
|
5116
5247
|
// src/index.ts
|
|
5117
|
-
var CLI_VERSION2 =
|
|
5248
|
+
var CLI_VERSION2 = readCliVersion();
|
|
5118
5249
|
var program = new Command();
|
|
5119
5250
|
program.name("avatar").description("AI harness CLI for NAL Vietnam engineering").version(CLI_VERSION2, "-v, --version", "Hi\u1EC3n th\u1ECB phi\xEAn b\u1EA3n Avatar CLI").addHelpText(
|
|
5120
5251
|
"beforeAll",
|
|
@@ -5142,6 +5273,7 @@ registerSecretsCommand(program);
|
|
|
5142
5273
|
registerMcpRunCommand(program);
|
|
5143
5274
|
registerAiCommand(program);
|
|
5144
5275
|
registerGitnexusCommand(program);
|
|
5276
|
+
registerPackCommand(program);
|
|
5145
5277
|
registerUninstallCommand(program);
|
|
5146
5278
|
program.parseAsync(process.argv).catch((err) => {
|
|
5147
5279
|
const msg = err instanceof Error ? err.message : String(err);
|