@nalvietnam/avatar-cli 1.10.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 +179 -59
- 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
|
|
|
@@ -3747,8 +3747,43 @@ async function findAlternativeWorkspaceName(parent, desiredName, maxAttempts = 1
|
|
|
3747
3747
|
return null;
|
|
3748
3748
|
}
|
|
3749
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
|
+
|
|
3750
3786
|
// src/commands/init-scaffold-variable-builders.ts
|
|
3751
|
-
var AVATAR_CLI_VERSION = "1.0.1";
|
|
3752
3787
|
function inferWorkspaceName(repoUrl) {
|
|
3753
3788
|
const trimmed = repoUrl.trim().replace(/\/+$/, "");
|
|
3754
3789
|
const lastSegment = trimmed.split(/[/:]/).pop() ?? "";
|
|
@@ -3794,7 +3829,7 @@ function buildScaffoldVariables(args) {
|
|
|
3794
3829
|
projectName: args.projectName,
|
|
3795
3830
|
projectDescription: args.projectDescription,
|
|
3796
3831
|
teamOwner: args.teamOwner,
|
|
3797
|
-
avatarVersion:
|
|
3832
|
+
avatarVersion: readCliVersion(),
|
|
3798
3833
|
packVersion: args.packVersion,
|
|
3799
3834
|
lastScan: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3800
3835
|
mode: args.mode,
|
|
@@ -3989,7 +4024,7 @@ async function runLogin(opts) {
|
|
|
3989
4024
|
log.success(`L\u01B0u credential v\xE0o ${USER_CONFIG_PATH} (chmod 600)`);
|
|
3990
4025
|
}
|
|
3991
4026
|
function sleep(ms) {
|
|
3992
|
-
return new Promise((
|
|
4027
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
3993
4028
|
}
|
|
3994
4029
|
|
|
3995
4030
|
// src/commands/init.ts
|
|
@@ -4097,7 +4132,7 @@ async function runInitFromExistingRemote(opts, ownerEmail) {
|
|
|
4097
4132
|
const teamOwner = opts.teamOwner ?? await promptTeamOwner(ownerEmail);
|
|
4098
4133
|
const inferredName = inferWorkspaceName(remoteUrl);
|
|
4099
4134
|
const workspaceName = opts.workspaceName ?? await input5({ message: "T\xEAn workspace:", default: inferredName });
|
|
4100
|
-
const workspaceParent =
|
|
4135
|
+
const workspaceParent = resolve2(opts.workspaceParent ?? ".");
|
|
4101
4136
|
const workspacePath = await resolveWorkspacePath(workspaceParent, workspaceName, opts.force);
|
|
4102
4137
|
await scaffoldWorkspaceWithSrcSubmodule({
|
|
4103
4138
|
workspacePath,
|
|
@@ -4120,7 +4155,7 @@ async function runInitFromExistingRemote(opts, ownerEmail) {
|
|
|
4120
4155
|
});
|
|
4121
4156
|
}
|
|
4122
4157
|
async function runInitFromExistingFolder(opts, ownerEmail) {
|
|
4123
|
-
const folderPath =
|
|
4158
|
+
const folderPath = resolve2(
|
|
4124
4159
|
opts.folderPath ?? await input5({
|
|
4125
4160
|
message: "\u0110\u01B0\u1EDDng d\u1EABn folder hi\u1EC7n c\xF3:",
|
|
4126
4161
|
validate: (v) => v.length > 0 ? true : "Path b\u1EAFt bu\u1ED9c"
|
|
@@ -4134,7 +4169,7 @@ async function runInitFromExistingFolder(opts, ownerEmail) {
|
|
|
4134
4169
|
const teamOwner = opts.teamOwner ?? await promptTeamOwner(ownerEmail);
|
|
4135
4170
|
const inferredName = opts.workspaceName ?? `${basename(folderPath)}-avatar-workspace`;
|
|
4136
4171
|
const workspaceName = opts.workspaceName ?? await input5({ message: "T\xEAn workspace:", default: inferredName });
|
|
4137
|
-
const workspaceParent =
|
|
4172
|
+
const workspaceParent = resolve2(opts.workspaceParent ?? ".");
|
|
4138
4173
|
const workspacePath = await resolveWorkspacePath(workspaceParent, workspaceName, opts.force);
|
|
4139
4174
|
await scaffoldWorkspaceWithSrcSubmodule({
|
|
4140
4175
|
workspacePath,
|
|
@@ -4171,9 +4206,9 @@ async function runInitFromScratch(opts, ownerEmail) {
|
|
|
4171
4206
|
]
|
|
4172
4207
|
});
|
|
4173
4208
|
const teamOwner = opts.teamOwner ?? await promptTeamOwner(ownerEmail);
|
|
4174
|
-
const workspaceParent =
|
|
4209
|
+
const workspaceParent = resolve2(opts.workspaceParent ?? ".");
|
|
4175
4210
|
const workspacePath = await resolveWorkspacePath(workspaceParent, projectName, opts.force);
|
|
4176
|
-
const srcPath =
|
|
4211
|
+
const srcPath = join25(workspacePath, "src");
|
|
4177
4212
|
await ensureDir(workspacePath);
|
|
4178
4213
|
await ensureDir(srcPath);
|
|
4179
4214
|
await safeBootstrapGitInFolder(srcPath, { autoYes: true });
|
|
@@ -4315,10 +4350,10 @@ async function finalizeWorkspaceScaffold(args) {
|
|
|
4315
4350
|
await writeRootClaudeMd(args.workspacePath, vars);
|
|
4316
4351
|
await writeProjectSettings(args.workspacePath, vars);
|
|
4317
4352
|
await appendGitignoreEntries(args.workspacePath);
|
|
4318
|
-
await ensureDir(
|
|
4319
|
-
await ensureDir(
|
|
4320
|
-
await installGitHook(
|
|
4321
|
-
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");
|
|
4322
4357
|
log.success("C\xE0i post-merge (workspace) + pre-push (src/)");
|
|
4323
4358
|
await autoSyncPackOnInit(args.workspacePath);
|
|
4324
4359
|
await appendAuditEntry("init", `flow=${args.flow},workspace=${args.workspaceName}`);
|
|
@@ -4356,12 +4391,12 @@ async function finalizeWorkspaceScaffold(args) {
|
|
|
4356
4391
|
await printInitSuccessBox(args.workspacePath, args.flow, aiResult, gitnexusResult);
|
|
4357
4392
|
}
|
|
4358
4393
|
async function autoSyncPackOnInit(workspacePath) {
|
|
4359
|
-
const packDir =
|
|
4394
|
+
const packDir = join25(workspacePath, TEAM_PACK_RELATIVE_PATH);
|
|
4360
4395
|
if (!await pathExists(packDir)) {
|
|
4361
4396
|
log.dim("Pack submodule kh\xF4ng t\u1ED3n t\u1EA1i (skip auto-sync). C\xF3 th\u1EC3 ch\u1EA1y `avatar sync` sau.");
|
|
4362
4397
|
return;
|
|
4363
4398
|
}
|
|
4364
|
-
const claudeDir =
|
|
4399
|
+
const claudeDir = join25(workspacePath, ".claude");
|
|
4365
4400
|
log.info("Auto-sync pack content v\xE0o .claude/ (symlinks + settings merge)...");
|
|
4366
4401
|
try {
|
|
4367
4402
|
const results = await syncAllMountDirs(packDir, claudeDir, false);
|
|
@@ -4475,7 +4510,7 @@ async function maybeCreateWorkspaceRemote(args) {
|
|
|
4475
4510
|
}
|
|
4476
4511
|
}
|
|
4477
4512
|
async function resolveWorkspacePath(parent, desiredName, force) {
|
|
4478
|
-
const desired =
|
|
4513
|
+
const desired = join25(parent, desiredName);
|
|
4479
4514
|
if (await isEmptyOrMissing(desired)) return desired;
|
|
4480
4515
|
log.warn(`Workspace path "${desired}" \u0111\xE3 c\xF3 n\u1ED9i dung.`);
|
|
4481
4516
|
while (true) {
|
|
@@ -4506,7 +4541,7 @@ async function resolveWorkspacePath(parent, desiredName, force) {
|
|
|
4506
4541
|
message: "T\xEAn workspace m\u1EDBi:",
|
|
4507
4542
|
validate: (v) => v.trim().length > 0 ? true : "T\xEAn kh\xF4ng \u0111\u01B0\u1EE3c r\u1ED7ng"
|
|
4508
4543
|
});
|
|
4509
|
-
const newPath =
|
|
4544
|
+
const newPath = join25(parent, newName.trim());
|
|
4510
4545
|
if (await isEmptyOrMissing(newPath)) return newPath;
|
|
4511
4546
|
log.warn(`"${newPath}" c\u0169ng \u0111\xE3 c\xF3 n\u1ED9i dung. Th\u1EED t\xEAn kh\xE1c.`);
|
|
4512
4547
|
}
|
|
@@ -4563,7 +4598,7 @@ async function printInitSuccessBox(rootPath, flow, aiResult = null, gitnexusResu
|
|
|
4563
4598
|
];
|
|
4564
4599
|
process.stdout.write(`${boxen6(lines.join("\n"), { padding: 1, borderStyle: "round" })}
|
|
4565
4600
|
`);
|
|
4566
|
-
const packDir =
|
|
4601
|
+
const packDir = join25(rootPath, TEAM_PACK_RELATIVE_PATH);
|
|
4567
4602
|
if (await pathExists(packDir)) {
|
|
4568
4603
|
process.stdout.write(`
|
|
4569
4604
|
${formatPackCommandsCheatsheetBox()}
|
|
@@ -4592,6 +4627,91 @@ function registerMcpRunCommand(program2) {
|
|
|
4592
4627
|
program2.command("mcp-run <tool-id>", { hidden: true }).description("[internal] Spawn MCP v\u1EDBi secrets injected (M09)").action(notImplementedYet("mcp-run", "Milestone 09"));
|
|
4593
4628
|
}
|
|
4594
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
|
+
|
|
4595
4715
|
// src/commands/restore.ts
|
|
4596
4716
|
function registerRestoreCommand(program2) {
|
|
4597
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"));
|
|
@@ -4619,22 +4739,21 @@ function registerSecretsCommand(program2) {
|
|
|
4619
4739
|
|
|
4620
4740
|
// src/commands/status.ts
|
|
4621
4741
|
import { promises as fs13 } from "fs";
|
|
4622
|
-
import { join as
|
|
4623
|
-
import
|
|
4742
|
+
import { join as join28 } from "path";
|
|
4743
|
+
import boxen8 from "boxen";
|
|
4624
4744
|
|
|
4625
4745
|
// src/lib/pack-backup-manager.ts
|
|
4626
4746
|
import { promises as fs12 } from "fs";
|
|
4627
|
-
import { join as
|
|
4747
|
+
import { join as join27 } from "path";
|
|
4628
4748
|
var BACKUP_DIR_NAME = "_backup";
|
|
4629
4749
|
async function listBackups(projectRoot) {
|
|
4630
|
-
const dir =
|
|
4750
|
+
const dir = join27(projectRoot, ".claude", BACKUP_DIR_NAME);
|
|
4631
4751
|
if (!await pathExists(dir)) return [];
|
|
4632
4752
|
const entries = await fs12.readdir(dir, { withFileTypes: true });
|
|
4633
4753
|
return entries.filter((e) => e.isDirectory()).map((e) => e.name).sort().reverse();
|
|
4634
4754
|
}
|
|
4635
4755
|
|
|
4636
4756
|
// src/commands/status.ts
|
|
4637
|
-
var AVATAR_CLI_VERSION2 = "1.0.1";
|
|
4638
4757
|
function registerStatusCommand(program2) {
|
|
4639
4758
|
program2.command("status").description("Snapshot t\u1EE9c th\xEC: project, pack version, pending, backup").option("--json", "Output JSON cho script").action(async (opts) => {
|
|
4640
4759
|
try {
|
|
@@ -4653,12 +4772,12 @@ function registerStatusCommand(program2) {
|
|
|
4653
4772
|
}
|
|
4654
4773
|
async function gatherStatus(cwd) {
|
|
4655
4774
|
const projectName = cwd.split("/").filter(Boolean).pop() ?? "unknown";
|
|
4656
|
-
const claudeRoot =
|
|
4775
|
+
const claudeRoot = join28(cwd, ".claude");
|
|
4657
4776
|
const hasAvatar = await pathExists(claudeRoot);
|
|
4658
4777
|
if (!hasAvatar) {
|
|
4659
4778
|
return {
|
|
4660
4779
|
projectName,
|
|
4661
|
-
cliVersion:
|
|
4780
|
+
cliVersion: readCliVersion(),
|
|
4662
4781
|
packVersion: null,
|
|
4663
4782
|
pendingCount: 0,
|
|
4664
4783
|
backupCount: 0,
|
|
@@ -4666,14 +4785,14 @@ async function gatherStatus(cwd) {
|
|
|
4666
4785
|
hasAvatar: false
|
|
4667
4786
|
};
|
|
4668
4787
|
}
|
|
4669
|
-
const packVersion = await isGitRepo(
|
|
4670
|
-
const pendingDir =
|
|
4788
|
+
const packVersion = await isGitRepo(join28(claudeRoot, "pack")) ? await readPinnedPackVersion(cwd).catch(() => null) : null;
|
|
4789
|
+
const pendingDir = join28(claudeRoot, "_pending");
|
|
4671
4790
|
const pendingCount = await pathExists(pendingDir) ? (await fs13.readdir(pendingDir)).filter((n) => n.endsWith(".diff.md")).length : 0;
|
|
4672
4791
|
const backupCount = (await listBackups(cwd)).length;
|
|
4673
4792
|
const techStackSummary = await readTechStackFirstLine(claudeRoot);
|
|
4674
4793
|
return {
|
|
4675
4794
|
projectName,
|
|
4676
|
-
cliVersion:
|
|
4795
|
+
cliVersion: AVATAR_CLI_VERSION,
|
|
4677
4796
|
packVersion,
|
|
4678
4797
|
pendingCount,
|
|
4679
4798
|
backupCount,
|
|
@@ -4682,7 +4801,7 @@ async function gatherStatus(cwd) {
|
|
|
4682
4801
|
};
|
|
4683
4802
|
}
|
|
4684
4803
|
async function readTechStackFirstLine(claudeRoot) {
|
|
4685
|
-
const techStackPath =
|
|
4804
|
+
const techStackPath = join28(claudeRoot, "project", "tech-stack.md");
|
|
4686
4805
|
if (!await pathExists(techStackPath)) return "(no tech-stack.md)";
|
|
4687
4806
|
const content = await readText(techStackPath);
|
|
4688
4807
|
const firstNonHeaderLine = content.split("\n").find((l) => l.trim() && !l.startsWith("#") && !l.startsWith(">"));
|
|
@@ -4698,18 +4817,18 @@ function renderStatusBox(s) {
|
|
|
4698
4817
|
`${chalk.dim("Backups:")} ${s.backupCount}`,
|
|
4699
4818
|
`${chalk.dim("Tech stack:")} ${s.techStackSummary}`
|
|
4700
4819
|
];
|
|
4701
|
-
process.stdout.write(`${
|
|
4820
|
+
process.stdout.write(`${boxen8(lines.join("\n"), { padding: 1, borderStyle: "round" })}
|
|
4702
4821
|
`);
|
|
4703
4822
|
}
|
|
4704
4823
|
|
|
4705
4824
|
// src/commands/sync.ts
|
|
4706
|
-
import { join as
|
|
4825
|
+
import { join as join30 } from "path";
|
|
4707
4826
|
|
|
4708
4827
|
// src/lib/preview-team-pack-sync-changes-for-dry-run.ts
|
|
4709
|
-
import { join as
|
|
4828
|
+
import { join as join29 } from "path";
|
|
4710
4829
|
async function inspectMountDir(packDir, claudeDir, dir) {
|
|
4711
|
-
const source =
|
|
4712
|
-
const dest =
|
|
4830
|
+
const source = join29(packDir, dir);
|
|
4831
|
+
const dest = join29(claudeDir, dir);
|
|
4713
4832
|
if (!await pathExists(source)) return "source-missing";
|
|
4714
4833
|
if (!await pathExists(dest)) return "needs-creation";
|
|
4715
4834
|
const { promises: fs14 } = await import("fs");
|
|
@@ -4751,8 +4870,8 @@ async function buildSyncPreview(packDir, claudeDir, targetVersion) {
|
|
|
4751
4870
|
var DEFAULT_PACK_BRANCH2 = "main";
|
|
4752
4871
|
async function syncAction(opts) {
|
|
4753
4872
|
const projectRoot = process.cwd();
|
|
4754
|
-
const claudeDir =
|
|
4755
|
-
const packDir =
|
|
4873
|
+
const claudeDir = join30(projectRoot, ".claude");
|
|
4874
|
+
const packDir = join30(projectRoot, TEAM_PACK_RELATIVE_PATH);
|
|
4756
4875
|
if (!await pathExists(packDir)) {
|
|
4757
4876
|
log.error(
|
|
4758
4877
|
`team-ai-pack submodule ch\u01B0a \u0111\u01B0\u1EE3c kh\u1EDFi t\u1EA1o \u1EDF ${TEAM_PACK_RELATIVE_PATH}/.
|
|
@@ -4890,32 +5009,32 @@ function registerToolsCommand(program2) {
|
|
|
4890
5009
|
// src/commands/uninstall.ts
|
|
4891
5010
|
import { relative as relative4 } from "path";
|
|
4892
5011
|
import { confirm as confirm6 } from "@inquirer/prompts";
|
|
4893
|
-
import
|
|
5012
|
+
import boxen9 from "boxen";
|
|
4894
5013
|
|
|
4895
5014
|
// src/lib/create-uninstall-backup-snapshot.ts
|
|
4896
5015
|
import { cp, mkdir, writeFile } from "fs/promises";
|
|
4897
5016
|
import { homedir as homedir4 } from "os";
|
|
4898
|
-
import { basename as basename2, join as
|
|
4899
|
-
var UNINSTALL_BACKUPS_DIR =
|
|
5017
|
+
import { basename as basename2, join as join31 } from "path";
|
|
5018
|
+
var UNINSTALL_BACKUPS_DIR = join31(homedir4(), ".avatar", "uninstall-backups");
|
|
4900
5019
|
async function createUninstallBackupSnapshot(projectRoot, artifacts, avatarVersion) {
|
|
4901
5020
|
const projectName = basename2(projectRoot);
|
|
4902
5021
|
const timestamp2 = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
4903
|
-
const backupDir =
|
|
5022
|
+
const backupDir = join31(UNINSTALL_BACKUPS_DIR, `${projectName}-${timestamp2}`);
|
|
4904
5023
|
await mkdir(backupDir, { recursive: true, mode: 448 });
|
|
4905
5024
|
if (artifacts.claudeDir) {
|
|
4906
|
-
await cp(artifacts.claudeDir,
|
|
5025
|
+
await cp(artifacts.claudeDir, join31(backupDir, ".claude"), { recursive: true });
|
|
4907
5026
|
}
|
|
4908
5027
|
if (artifacts.claudeMd) {
|
|
4909
|
-
await cp(artifacts.claudeMd,
|
|
5028
|
+
await cp(artifacts.claudeMd, join31(backupDir, "CLAUDE.md"));
|
|
4910
5029
|
}
|
|
4911
5030
|
if (artifacts.postMergeHook || artifacts.prePushHook) {
|
|
4912
|
-
const hooksBackupDir =
|
|
5031
|
+
const hooksBackupDir = join31(backupDir, "hooks");
|
|
4913
5032
|
await mkdir(hooksBackupDir, { recursive: true });
|
|
4914
5033
|
if (artifacts.postMergeHook) {
|
|
4915
|
-
await cp(artifacts.postMergeHook,
|
|
5034
|
+
await cp(artifacts.postMergeHook, join31(hooksBackupDir, "post-merge"));
|
|
4916
5035
|
}
|
|
4917
5036
|
if (artifacts.prePushHook) {
|
|
4918
|
-
await cp(artifacts.prePushHook,
|
|
5037
|
+
await cp(artifacts.prePushHook, join31(hooksBackupDir, "pre-push"));
|
|
4919
5038
|
}
|
|
4920
5039
|
}
|
|
4921
5040
|
const manifest = {
|
|
@@ -4930,27 +5049,27 @@ async function createUninstallBackupSnapshot(projectRoot, artifacts, avatarVersi
|
|
|
4930
5049
|
prePushHook: !!artifacts.prePushHook
|
|
4931
5050
|
}
|
|
4932
5051
|
};
|
|
4933
|
-
await writeFile(
|
|
5052
|
+
await writeFile(join31(backupDir, "manifest.json"), JSON.stringify(manifest, null, 2), "utf8");
|
|
4934
5053
|
return backupDir;
|
|
4935
5054
|
}
|
|
4936
5055
|
|
|
4937
5056
|
// src/lib/detect-avatar-project-artifacts.ts
|
|
4938
5057
|
import { existsSync as existsSync9 } from "fs";
|
|
4939
|
-
import { join as
|
|
5058
|
+
import { join as join32 } from "path";
|
|
4940
5059
|
function existsOrNull(path) {
|
|
4941
5060
|
return existsSync9(path) ? path : null;
|
|
4942
5061
|
}
|
|
4943
5062
|
function detectAvatarProjectArtifacts(projectRoot) {
|
|
4944
|
-
const claudeDir = existsOrNull(
|
|
4945
|
-
const claudeMd = existsOrNull(
|
|
4946
|
-
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"));
|
|
4947
5066
|
const prePushHook = existsOrNull(
|
|
4948
|
-
|
|
5067
|
+
join32(projectRoot, ".git", "modules", "src", "hooks", "pre-push")
|
|
4949
5068
|
);
|
|
4950
|
-
const gitignorePath = existsOrNull(
|
|
4951
|
-
const gitmodulesPath = existsOrNull(
|
|
4952
|
-
const notesDir = existsOrNull(
|
|
4953
|
-
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"));
|
|
4954
5073
|
const hasAnyArtifact = !!(claudeDir || claudeMd || postMergeHook || prePushHook);
|
|
4955
5074
|
return {
|
|
4956
5075
|
hasAnyArtifact,
|
|
@@ -4971,11 +5090,11 @@ async function executeUninstallDeletion(artifacts, flags) {
|
|
|
4971
5090
|
if (artifacts.claudeDir) {
|
|
4972
5091
|
if (flags.keepSubmodule) {
|
|
4973
5092
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
4974
|
-
const { join:
|
|
5093
|
+
const { join: join33 } = await import("path");
|
|
4975
5094
|
const entries = await readdir2(artifacts.claudeDir);
|
|
4976
5095
|
for (const entry of entries) {
|
|
4977
5096
|
if (entry === "pack") continue;
|
|
4978
|
-
await rm(
|
|
5097
|
+
await rm(join33(artifacts.claudeDir, entry), { recursive: true, force: true });
|
|
4979
5098
|
}
|
|
4980
5099
|
} else {
|
|
4981
5100
|
await rm(artifacts.claudeDir, { recursive: true, force: true });
|
|
@@ -5121,12 +5240,12 @@ function printUninstallSuccessBox(backupPath) {
|
|
|
5121
5240
|
lines.push(` ${chalk.dim("Backup:")} ${backupPath}`);
|
|
5122
5241
|
lines.push(` ${chalk.dim("Restore:")} ${chalk.cyan(`cp -r "${backupPath}"/* .`)}`);
|
|
5123
5242
|
}
|
|
5124
|
-
process.stdout.write(`${
|
|
5243
|
+
process.stdout.write(`${boxen9(lines.join("\n"), { padding: 1, borderStyle: "round" })}
|
|
5125
5244
|
`);
|
|
5126
5245
|
}
|
|
5127
5246
|
|
|
5128
5247
|
// src/index.ts
|
|
5129
|
-
var CLI_VERSION2 =
|
|
5248
|
+
var CLI_VERSION2 = readCliVersion();
|
|
5130
5249
|
var program = new Command();
|
|
5131
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(
|
|
5132
5251
|
"beforeAll",
|
|
@@ -5154,6 +5273,7 @@ registerSecretsCommand(program);
|
|
|
5154
5273
|
registerMcpRunCommand(program);
|
|
5155
5274
|
registerAiCommand(program);
|
|
5156
5275
|
registerGitnexusCommand(program);
|
|
5276
|
+
registerPackCommand(program);
|
|
5157
5277
|
registerUninstallCommand(program);
|
|
5158
5278
|
program.parseAsync(process.argv).catch((err) => {
|
|
5159
5279
|
const msg = err instanceof Error ? err.message : String(err);
|