@nalvietnam/avatar-cli 1.4.0 → 1.4.1
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
CHANGED
|
@@ -183,6 +183,33 @@ function spinner(text) {
|
|
|
183
183
|
isEnabled: process.stdout.isTTY ?? false
|
|
184
184
|
}).start();
|
|
185
185
|
}
|
|
186
|
+
function spinnerWithElapsed(prefix) {
|
|
187
|
+
const startMs = Date.now();
|
|
188
|
+
const sp = spinner(`${prefix} (0:00)`);
|
|
189
|
+
const formatElapsed = () => {
|
|
190
|
+
const sec = Math.floor((Date.now() - startMs) / 1e3);
|
|
191
|
+
const m = Math.floor(sec / 60);
|
|
192
|
+
const s = sec % 60;
|
|
193
|
+
return `${m}:${String(s).padStart(2, "0")}`;
|
|
194
|
+
};
|
|
195
|
+
const interval = setInterval(() => {
|
|
196
|
+
sp.text = `${prefix} (${formatElapsed()})`;
|
|
197
|
+
}, 1e3);
|
|
198
|
+
return {
|
|
199
|
+
succeed: (text) => {
|
|
200
|
+
clearInterval(interval);
|
|
201
|
+
sp.succeed(`${text} (${formatElapsed()})`);
|
|
202
|
+
},
|
|
203
|
+
fail: (text) => {
|
|
204
|
+
clearInterval(interval);
|
|
205
|
+
sp.fail(`${text} (${formatElapsed()})`);
|
|
206
|
+
},
|
|
207
|
+
stop: () => {
|
|
208
|
+
clearInterval(interval);
|
|
209
|
+
sp.stop();
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
}
|
|
186
213
|
|
|
187
214
|
// src/lib/check-claude-code-subscription-and-quota.ts
|
|
188
215
|
var QUOTA_VERIFY_TIMEOUT_MS = 3e4;
|
|
@@ -1359,44 +1386,57 @@ function classifyOperationFailure(operation, exitCode, signal, stderrSample) {
|
|
|
1359
1386
|
stderrSample
|
|
1360
1387
|
);
|
|
1361
1388
|
}
|
|
1389
|
+
function tailLines(text, n) {
|
|
1390
|
+
const lines = text.split("\n");
|
|
1391
|
+
return lines.slice(-n).join("\n");
|
|
1392
|
+
}
|
|
1362
1393
|
function runGitnexusSetup() {
|
|
1363
|
-
|
|
1394
|
+
const sp = spinnerWithElapsed("Setup GitNexus global skills (~/.claude/skills/gitnexus-*)");
|
|
1364
1395
|
const result = spawnSync7("gitnexus", ["setup"], {
|
|
1365
|
-
stdio: ["
|
|
1396
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1366
1397
|
timeout: SETUP_TIMEOUT_MS,
|
|
1367
1398
|
encoding: "utf8"
|
|
1368
1399
|
});
|
|
1369
1400
|
if (result.status !== 0 || result.signal === "SIGTERM") {
|
|
1401
|
+
sp.fail("GitNexus setup failed");
|
|
1370
1402
|
const stderr = (result.stderr || "").trim();
|
|
1371
|
-
|
|
1403
|
+
const stdout = (result.stdout || "").trim();
|
|
1404
|
+
if (stderr) process.stderr.write(`${tailLines(stderr, 30)}
|
|
1405
|
+
`);
|
|
1406
|
+
else if (stdout) process.stderr.write(`${tailLines(stdout, 30)}
|
|
1372
1407
|
`);
|
|
1373
1408
|
throw classifyOperationFailure("setup", result.status, result.signal, stderr);
|
|
1374
1409
|
}
|
|
1375
|
-
|
|
1410
|
+
sp.succeed("GitNexus setup OK (global skills installed)");
|
|
1376
1411
|
}
|
|
1377
1412
|
function runGitnexusAnalyze(workspacePath) {
|
|
1378
|
-
|
|
1413
|
+
const sp = spinnerWithElapsed(`Analyze workspace ${workspacePath} (1-3 ph\xFAt)`);
|
|
1379
1414
|
const result = spawnSync7("gitnexus", ["analyze", "."], {
|
|
1380
1415
|
cwd: workspacePath,
|
|
1381
|
-
stdio: ["
|
|
1416
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1382
1417
|
timeout: ANALYZE_TIMEOUT_MS,
|
|
1383
1418
|
encoding: "utf8"
|
|
1384
1419
|
});
|
|
1385
1420
|
if (result.status !== 0 || result.signal === "SIGTERM") {
|
|
1421
|
+
sp.fail("Analyze failed");
|
|
1386
1422
|
const stderr = (result.stderr || "").trim();
|
|
1387
|
-
|
|
1423
|
+
const stdout = (result.stdout || "").trim();
|
|
1424
|
+
if (stderr) process.stderr.write(`${tailLines(stderr, 30)}
|
|
1425
|
+
`);
|
|
1426
|
+
else if (stdout) process.stderr.write(`${tailLines(stdout, 30)}
|
|
1388
1427
|
`);
|
|
1389
1428
|
throw classifyOperationFailure("analyze", result.status, result.signal, stderr);
|
|
1390
1429
|
}
|
|
1391
1430
|
const metaPath = join12(workspacePath, ".gitnexus", "meta.json");
|
|
1392
1431
|
if (!existsSync4(metaPath)) {
|
|
1432
|
+
sp.fail("Analyze exit 0 nh\u01B0ng kh\xF4ng th\u1EA5y meta.json");
|
|
1393
1433
|
throw new GitnexusOperationError(
|
|
1394
1434
|
"analyze",
|
|
1395
1435
|
"missing-output",
|
|
1396
1436
|
`gitnexus analyze xong nh\u01B0ng kh\xF4ng th\u1EA5y ${metaPath}. Repo c\xF3 th\u1EC3 empty ho\u1EB7c gitnexus fail silent.`
|
|
1397
1437
|
);
|
|
1398
1438
|
}
|
|
1399
|
-
|
|
1439
|
+
sp.succeed(`Analyze OK (index t\u1EA1i ${join12(workspacePath, ".gitnexus")})`);
|
|
1400
1440
|
}
|
|
1401
1441
|
|
|
1402
1442
|
// src/lib/run-gitnexus-setup-phase.ts
|
|
@@ -1590,6 +1630,7 @@ import { existsSync as existsSync5 } from "fs";
|
|
|
1590
1630
|
import { join as join14 } from "path";
|
|
1591
1631
|
import { confirm as confirm2 } from "@inquirer/prompts";
|
|
1592
1632
|
var WIKI_TIMEOUT_MS = 15 * 60 * 1e3;
|
|
1633
|
+
var DEFAULT_LLMLITE_MODEL = "nal-claude";
|
|
1593
1634
|
async function readSettingsForWikiCredentials(workspacePath) {
|
|
1594
1635
|
const settingsPath = join14(workspacePath, ".claude", "settings.json");
|
|
1595
1636
|
if (!await pathExists(settingsPath)) return null;
|
|
@@ -1599,17 +1640,22 @@ async function readSettingsForWikiCredentials(workspacePath) {
|
|
|
1599
1640
|
const apiKey = typeof env.ANTHROPIC_AUTH_TOKEN === "string" ? env.ANTHROPIC_AUTH_TOKEN : null;
|
|
1600
1641
|
const baseUrl = typeof env.ANTHROPIC_BASE_URL === "string" ? env.ANTHROPIC_BASE_URL : null;
|
|
1601
1642
|
if (!apiKey || !baseUrl) return null;
|
|
1602
|
-
|
|
1643
|
+
const model = typeof env.ANTHROPIC_MODEL === "string" && env.ANTHROPIC_MODEL.length > 0 ? env.ANTHROPIC_MODEL : DEFAULT_LLMLITE_MODEL;
|
|
1644
|
+
return { apiKey, baseUrl, model };
|
|
1603
1645
|
} catch {
|
|
1604
1646
|
return null;
|
|
1605
1647
|
}
|
|
1606
1648
|
}
|
|
1607
|
-
async function confirmWikiGeneration(baseUrl) {
|
|
1649
|
+
async function confirmWikiGeneration(baseUrl, model) {
|
|
1608
1650
|
return await confirm2({
|
|
1609
|
-
message: `Generate wiki cho workspace? (~$0.50 qua ${baseUrl}, 2-5 ph\xFAt). Continue?`,
|
|
1651
|
+
message: `Generate wiki cho workspace? (~$0.50 qua ${baseUrl} model=${model}, 2-5 ph\xFAt). Continue?`,
|
|
1610
1652
|
default: true
|
|
1611
1653
|
});
|
|
1612
1654
|
}
|
|
1655
|
+
function tailLines2(text, n) {
|
|
1656
|
+
const lines = text.split("\n");
|
|
1657
|
+
return lines.slice(-n).join("\n");
|
|
1658
|
+
}
|
|
1613
1659
|
async function runGitnexusWikiConditional(workspacePath) {
|
|
1614
1660
|
const creds = await readSettingsForWikiCredentials(workspacePath);
|
|
1615
1661
|
if (!creds) {
|
|
@@ -1617,29 +1663,33 @@ async function runGitnexusWikiConditional(workspacePath) {
|
|
|
1617
1663
|
log.dim("\u0110\u1EC3 gen wiki sau, ch\u1EA1y manual: gitnexus wiki . --api-key <openai-key>");
|
|
1618
1664
|
return { ran: false, skipped: true, reason: "subscription-mode" };
|
|
1619
1665
|
}
|
|
1620
|
-
const proceed = await confirmWikiGeneration(creds.baseUrl);
|
|
1666
|
+
const proceed = await confirmWikiGeneration(creds.baseUrl, creds.model);
|
|
1621
1667
|
if (!proceed) {
|
|
1622
1668
|
log.dim(
|
|
1623
1669
|
"User decline wiki gen \u2014 workspace OK kh\xF4ng c\xF3 wiki. Ch\u1EA1y `gitnexus wiki` manual sau khi c\u1EA7n."
|
|
1624
1670
|
);
|
|
1625
1671
|
return { ran: false, skipped: true, reason: "user-declined" };
|
|
1626
1672
|
}
|
|
1627
|
-
|
|
1673
|
+
const sp = spinnerWithElapsed(`Generating wiki via ${creds.baseUrl} model=${creds.model}`);
|
|
1628
1674
|
const result = spawnSync10(
|
|
1629
1675
|
"gitnexus",
|
|
1630
|
-
["wiki", ".", "--api-key", creds.apiKey, "--base-url", creds.baseUrl],
|
|
1676
|
+
["wiki", ".", "--api-key", creds.apiKey, "--base-url", creds.baseUrl, "--model", creds.model],
|
|
1631
1677
|
{
|
|
1632
1678
|
cwd: workspacePath,
|
|
1633
|
-
stdio: ["
|
|
1679
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1634
1680
|
timeout: WIKI_TIMEOUT_MS,
|
|
1635
1681
|
encoding: "utf8"
|
|
1636
1682
|
}
|
|
1637
1683
|
);
|
|
1638
1684
|
if (result.status !== 0 || result.signal === "SIGTERM") {
|
|
1685
|
+
const reason = result.signal === "SIGTERM" ? "timeout" : "non-zero-exit";
|
|
1686
|
+
sp.fail(`Wiki gen ${reason} (exit ${result.status ?? "null"})`);
|
|
1639
1687
|
const stderr = (result.stderr || "").trim();
|
|
1640
|
-
|
|
1688
|
+
const stdout = (result.stdout || "").trim();
|
|
1689
|
+
if (stderr) process.stderr.write(`${tailLines2(stderr, 30)}
|
|
1690
|
+
`);
|
|
1691
|
+
else if (stdout) process.stderr.write(`${tailLines2(stdout, 30)}
|
|
1641
1692
|
`);
|
|
1642
|
-
const reason = result.signal === "SIGTERM" ? "timeout" : "non-zero-exit";
|
|
1643
1693
|
return {
|
|
1644
1694
|
ran: false,
|
|
1645
1695
|
skipped: true,
|
|
@@ -1649,6 +1699,7 @@ async function runGitnexusWikiConditional(workspacePath) {
|
|
|
1649
1699
|
}
|
|
1650
1700
|
const wikiPath = join14(workspacePath, ".gitnexus", "wiki", "index.html");
|
|
1651
1701
|
if (!existsSync5(wikiPath)) {
|
|
1702
|
+
sp.fail("Wiki exit 0 nh\u01B0ng kh\xF4ng th\u1EA5y index.html");
|
|
1652
1703
|
return {
|
|
1653
1704
|
ran: false,
|
|
1654
1705
|
skipped: true,
|
|
@@ -1656,7 +1707,7 @@ async function runGitnexusWikiConditional(workspacePath) {
|
|
|
1656
1707
|
detail: `Wiki exit 0 nh\u01B0ng kh\xF4ng th\u1EA5y ${wikiPath}`
|
|
1657
1708
|
};
|
|
1658
1709
|
}
|
|
1659
|
-
|
|
1710
|
+
sp.succeed(`Wiki ready: ${wikiPath}`);
|
|
1660
1711
|
return { ran: true, skipped: false, wikiPath };
|
|
1661
1712
|
}
|
|
1662
1713
|
|
|
@@ -4132,7 +4183,7 @@ async function removeSubmoduleEntry(gitmodulesPath, submodulePath) {
|
|
|
4132
4183
|
}
|
|
4133
4184
|
|
|
4134
4185
|
// src/commands/uninstall.ts
|
|
4135
|
-
var CLI_VERSION = "1.4.
|
|
4186
|
+
var CLI_VERSION = "1.4.1";
|
|
4136
4187
|
function registerUninstallCommand(program2) {
|
|
4137
4188
|
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) => {
|
|
4138
4189
|
try {
|
|
@@ -4214,7 +4265,7 @@ function printUninstallSuccessBox(backupPath) {
|
|
|
4214
4265
|
}
|
|
4215
4266
|
|
|
4216
4267
|
// src/index.ts
|
|
4217
|
-
var CLI_VERSION2 = "1.4.
|
|
4268
|
+
var CLI_VERSION2 = "1.4.1";
|
|
4218
4269
|
var program = new Command();
|
|
4219
4270
|
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(
|
|
4220
4271
|
"beforeAll",
|