@kyubiware/commit-mint 0.7.2 → 0.7.4
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/README.md +13 -0
- package/dist/cli.mjs +192 -22
- package/dist/cli.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -31,10 +31,23 @@ API key. The key is saved to `~/.commit-mint` (INI).
|
|
|
31
31
|
cmint # interactive: stage → checks → review → commit
|
|
32
32
|
cmint -a # auto-group, generate messages, commit everything
|
|
33
33
|
cmint config # edit provider, model, locale, etc.
|
|
34
|
+
cmint update # update cmint to the latest published version
|
|
34
35
|
```
|
|
35
36
|
|
|
36
37
|
See [all flags](#all-flags) for the full list.
|
|
37
38
|
|
|
39
|
+
## Self-update (`cmint update`)
|
|
40
|
+
|
|
41
|
+
`cmint update` checks the npm registry for a newer version and, if one exists,
|
|
42
|
+
runs the appropriate global install command for your package manager (detected
|
|
43
|
+
from `npm_config_user_agent` — npm, pnpm, yarn, or bun; falls back to npm).
|
|
44
|
+
|
|
45
|
+
- If you're already on the latest version, it exits silently.
|
|
46
|
+
- Otherwise it prints `current → latest` and asks for confirmation before
|
|
47
|
+
running the install (live npm output is streamed to your terminal).
|
|
48
|
+
- `cmint update -y` (or `--yes`) skips the confirmation prompt — useful in
|
|
49
|
+
scripts.
|
|
50
|
+
|
|
38
51
|
## Pre-flight checks (`.cmintrc`)
|
|
39
52
|
|
|
40
53
|
`.cmintrc` is commit-mint's pre-commit check system. The config syntax is
|
package/dist/cli.mjs
CHANGED
|
@@ -29,7 +29,7 @@ var __exportAll = (all, no_symbols) => {
|
|
|
29
29
|
//#region package.json
|
|
30
30
|
var package_default = {
|
|
31
31
|
name: "@kyubiware/commit-mint",
|
|
32
|
-
version: "0.7.
|
|
32
|
+
version: "0.7.4",
|
|
33
33
|
description: "🌿 AI-powered git commit tool — auto-group changed files, generate messages, run pre-commit checks",
|
|
34
34
|
type: "module",
|
|
35
35
|
bin: { "cmint": "./dist/cli.mjs" },
|
|
@@ -2311,7 +2311,6 @@ async function agentCommand(flags) {
|
|
|
2311
2311
|
//#endregion
|
|
2312
2312
|
//#region src/services/update-check.ts
|
|
2313
2313
|
const REGISTRY_URL = "https://registry.npmjs.org/-/package/@kyubiware/commit-mint/dist-tags";
|
|
2314
|
-
const PACKAGE_NAME = "@kyubiware/commit-mint";
|
|
2315
2314
|
const TTL_MS = 1440 * 60 * 1e3;
|
|
2316
2315
|
const FETCH_TIMEOUT_MS = 5e3;
|
|
2317
2316
|
let cachePath = join(os.homedir(), ".cache", "commit-mint", "update-check.json");
|
|
@@ -2396,7 +2395,7 @@ async function fetchLatest(parentSignal) {
|
|
|
2396
2395
|
}
|
|
2397
2396
|
function displayNag(current, latest) {
|
|
2398
2397
|
debug("displayNag: %s → %s", current, latest);
|
|
2399
|
-
const message = `Update available: ${yellow(current)} → ${green(latest)}\nRun ${cyan(
|
|
2398
|
+
const message = `Update available: ${yellow(current)} → ${green(latest)}\nRun ${cyan("cmint update")} to update`;
|
|
2400
2399
|
log.warn(message);
|
|
2401
2400
|
}
|
|
2402
2401
|
/**
|
|
@@ -2404,11 +2403,16 @@ function displayNag(current, latest) {
|
|
|
2404
2403
|
* {@link checkForUpdatesUpfront}. Accepts an optional AbortSignal that
|
|
2405
2404
|
* propagates to the underlying fetch — used by the cancellable spinner.
|
|
2406
2405
|
*
|
|
2406
|
+
* When an update is available, {@link onNag} is invoked (defaulting to
|
|
2407
|
+
* {@link displayNag}). The cancellable spinner path passes a capturing
|
|
2408
|
+
* callback so it can stop the spinner BEFORE the nag prints — otherwise
|
|
2409
|
+
* `log.warn` interleaves with the spinner line and leaves it on screen.
|
|
2410
|
+
*
|
|
2407
2411
|
* Returns an {@link UpdateCheckStatus} so the caller can distinguish a
|
|
2408
2412
|
* real fetch that found the user current (eligible for "You are on the
|
|
2409
2413
|
* latest version" feedback) from a silent cache hit.
|
|
2410
2414
|
*/
|
|
2411
|
-
async function runUpdateCheck(currentVersion, parentSignal) {
|
|
2415
|
+
async function runUpdateCheck(currentVersion, parentSignal, onNag = displayNag) {
|
|
2412
2416
|
debug("runUpdateCheck: currentVersion=%s", currentVersion);
|
|
2413
2417
|
if (shouldSkip(currentVersion)) {
|
|
2414
2418
|
debug("runUpdateCheck: skipped (NO_UPDATE_NOTIFIER / CI / NODE_ENV=test / non-TTY / invalid version)");
|
|
@@ -2419,7 +2423,7 @@ async function runUpdateCheck(currentVersion, parentSignal) {
|
|
|
2419
2423
|
if (cached && Date.now() - cached.checkedAt < TTL_MS) {
|
|
2420
2424
|
debug("runUpdateCheck: cache fresh (<%dh), skipping fetch", TTL_MS / 36e5);
|
|
2421
2425
|
if (semver.gt(cached.latest, currentVersion)) {
|
|
2422
|
-
|
|
2426
|
+
onNag(currentVersion, cached.latest);
|
|
2423
2427
|
return "cache-update";
|
|
2424
2428
|
}
|
|
2425
2429
|
debug("runUpdateCheck: current >= latest, no nag");
|
|
@@ -2437,7 +2441,7 @@ async function runUpdateCheck(currentVersion, parentSignal) {
|
|
|
2437
2441
|
checkedAt: Date.now()
|
|
2438
2442
|
});
|
|
2439
2443
|
if (semver.gt(latest, currentVersion)) {
|
|
2440
|
-
|
|
2444
|
+
onNag(currentVersion, latest);
|
|
2441
2445
|
return "fetch-update";
|
|
2442
2446
|
}
|
|
2443
2447
|
debug("runUpdateCheck: current >= latest, no nag");
|
|
@@ -2560,9 +2564,15 @@ async function runCheckWithSpinner(currentVersion) {
|
|
|
2560
2564
|
reportFetchCurrent(await runUpdateCheck(currentVersion));
|
|
2561
2565
|
return;
|
|
2562
2566
|
}
|
|
2567
|
+
const captured = { nag: null };
|
|
2563
2568
|
let status;
|
|
2564
2569
|
try {
|
|
2565
|
-
status = await runUpdateCheck(currentVersion, controller.signal)
|
|
2570
|
+
status = await runUpdateCheck(currentVersion, controller.signal, (current, latest) => {
|
|
2571
|
+
captured.nag = {
|
|
2572
|
+
current,
|
|
2573
|
+
latest
|
|
2574
|
+
};
|
|
2575
|
+
});
|
|
2566
2576
|
} finally {
|
|
2567
2577
|
handler.cleanup();
|
|
2568
2578
|
}
|
|
@@ -2570,8 +2580,10 @@ async function runCheckWithSpinner(currentVersion) {
|
|
|
2570
2580
|
debug("runCheckWithSpinner: spinner dismissed by user");
|
|
2571
2581
|
s.stop("Skipped");
|
|
2572
2582
|
} else if (status === "fetch-current") s.stop(green("You are on the latest version"));
|
|
2573
|
-
else if (status === "fetch-update"
|
|
2574
|
-
|
|
2583
|
+
else if (status === "fetch-update" || status === "cache-update") {
|
|
2584
|
+
s.stop("");
|
|
2585
|
+
if (captured.nag) displayNag(captured.nag.current, captured.nag.latest);
|
|
2586
|
+
} else if (status === "fetch-failed-or-aborted") s.stop("Update check failed");
|
|
2575
2587
|
else s.stop("");
|
|
2576
2588
|
}
|
|
2577
2589
|
//#endregion
|
|
@@ -3372,6 +3384,149 @@ async function logsCommand(flags) {
|
|
|
3372
3384
|
for (const line of lines) console.log(line);
|
|
3373
3385
|
}
|
|
3374
3386
|
//#endregion
|
|
3387
|
+
//#region src/services/updater.ts
|
|
3388
|
+
const PACKAGE_NAME = "@kyubiware/commit-mint";
|
|
3389
|
+
/**
|
|
3390
|
+
* Detect the active package manager from the `npm_config_user_agent` env var.
|
|
3391
|
+
* Format: "<pm>/<version> <...>" — e.g. "npm/10.2.0 node/v20.10.0".
|
|
3392
|
+
* Empty/undefined input and unrecognized prefixes fall back to "npm".
|
|
3393
|
+
*/
|
|
3394
|
+
function detectPackageManager(userAgent) {
|
|
3395
|
+
if (!userAgent) {
|
|
3396
|
+
debug("updater: empty userAgent, defaulting to npm");
|
|
3397
|
+
return "npm";
|
|
3398
|
+
}
|
|
3399
|
+
const prefix = userAgent.split("/")[0];
|
|
3400
|
+
switch (prefix) {
|
|
3401
|
+
case "pnpm": return "pnpm";
|
|
3402
|
+
case "yarn": return "yarn";
|
|
3403
|
+
case "bun": return "bun";
|
|
3404
|
+
case "npm": return "npm";
|
|
3405
|
+
default:
|
|
3406
|
+
debug("updater: unknown userAgent prefix '%s', defaulting to npm", prefix);
|
|
3407
|
+
return "npm";
|
|
3408
|
+
}
|
|
3409
|
+
}
|
|
3410
|
+
/**
|
|
3411
|
+
* Build the global install shell command for the given package manager.
|
|
3412
|
+
* Returned as a single string suitable for `execa(cmd, [], { shell: true })`.
|
|
3413
|
+
*/
|
|
3414
|
+
function buildUpdateCommand(pm, packageName = PACKAGE_NAME) {
|
|
3415
|
+
switch (pm) {
|
|
3416
|
+
case "npm": return `npm install -g ${packageName}@latest`;
|
|
3417
|
+
case "pnpm": return `pnpm add -g ${packageName}@latest`;
|
|
3418
|
+
case "yarn": return `yarn global add ${packageName}@latest`;
|
|
3419
|
+
case "bun": return `bun add -g ${packageName}@latest`;
|
|
3420
|
+
}
|
|
3421
|
+
}
|
|
3422
|
+
/**
|
|
3423
|
+
* Fetch the latest version from the npm registry via `npm view <pkg> version`.
|
|
3424
|
+
* Returns the trimmed version string on success, or null on any failure
|
|
3425
|
+
* (non-zero exit, empty stdout, thrown error). Never throws.
|
|
3426
|
+
*/
|
|
3427
|
+
async function fetchLatestVersion(packageName = PACKAGE_NAME) {
|
|
3428
|
+
debug("updater: fetching latest version for %s", packageName);
|
|
3429
|
+
try {
|
|
3430
|
+
const result = await execa("npm", [
|
|
3431
|
+
"view",
|
|
3432
|
+
packageName,
|
|
3433
|
+
"version"
|
|
3434
|
+
], { reject: false });
|
|
3435
|
+
if (result.exitCode !== 0) {
|
|
3436
|
+
debug("updater: npm view exited %d", result.exitCode);
|
|
3437
|
+
return null;
|
|
3438
|
+
}
|
|
3439
|
+
const trimmed = result.stdout.trim();
|
|
3440
|
+
if (!trimmed) {
|
|
3441
|
+
debug("updater: npm view returned empty stdout");
|
|
3442
|
+
return null;
|
|
3443
|
+
}
|
|
3444
|
+
debug("updater: latest=%s", trimmed);
|
|
3445
|
+
return trimmed;
|
|
3446
|
+
} catch (err) {
|
|
3447
|
+
debug("updater: fetchLatestVersion error — %s", err instanceof Error ? err.message : String(err));
|
|
3448
|
+
return null;
|
|
3449
|
+
}
|
|
3450
|
+
}
|
|
3451
|
+
/**
|
|
3452
|
+
* True iff `latest` is strictly greater than `current` per semver. Returns
|
|
3453
|
+
* false on invalid semver input rather than throwing.
|
|
3454
|
+
*/
|
|
3455
|
+
function isUpdateAvailable(current, latest) {
|
|
3456
|
+
try {
|
|
3457
|
+
return semver.gt(latest, current);
|
|
3458
|
+
} catch (err) {
|
|
3459
|
+
debug("updater: isUpdateAvailable error — %s", err instanceof Error ? err.message : String(err));
|
|
3460
|
+
return false;
|
|
3461
|
+
}
|
|
3462
|
+
}
|
|
3463
|
+
/**
|
|
3464
|
+
* Run the global install command for the given package manager. Streams
|
|
3465
|
+
* the installer's live output to the user's terminal via `stdio: "inherit"`.
|
|
3466
|
+
* Returns true iff the install exits with code 0.
|
|
3467
|
+
*/
|
|
3468
|
+
async function runUpdate(pm, packageName = PACKAGE_NAME) {
|
|
3469
|
+
const command = buildUpdateCommand(pm, packageName);
|
|
3470
|
+
debug("updater: running %s", command);
|
|
3471
|
+
try {
|
|
3472
|
+
return (await execa(command, [], {
|
|
3473
|
+
shell: true,
|
|
3474
|
+
reject: false,
|
|
3475
|
+
stdio: "inherit"
|
|
3476
|
+
})).exitCode === 0;
|
|
3477
|
+
} catch (err) {
|
|
3478
|
+
debug("updater: runUpdate error — %s", err instanceof Error ? err.message : String(err));
|
|
3479
|
+
return false;
|
|
3480
|
+
}
|
|
3481
|
+
}
|
|
3482
|
+
//#endregion
|
|
3483
|
+
//#region src/commands/update.ts
|
|
3484
|
+
/**
|
|
3485
|
+
* Self-update flow for the installed `cmint` package. Detects the active
|
|
3486
|
+
* package manager from `npm_config_user_agent`, asks the npm registry for the
|
|
3487
|
+
* latest published version, and — if newer — runs the equivalent global
|
|
3488
|
+
* install command after a confirmation prompt (skippable with `--yes`).
|
|
3489
|
+
*
|
|
3490
|
+
* Never throws. Registry failures and install failures are reported through
|
|
3491
|
+
* `p.outro(...)` and `process.exit(1)`; cancellation and "already current"
|
|
3492
|
+
* resolve normally so the CLI exits cleanly.
|
|
3493
|
+
*/
|
|
3494
|
+
async function updateCommand(currentVersion, flags) {
|
|
3495
|
+
p.intro("cmint update");
|
|
3496
|
+
const pm = detectPackageManager(process.env.npm_config_user_agent);
|
|
3497
|
+
p.log.info(`Package manager: ${pm}`);
|
|
3498
|
+
p.log.message("Checking latest version...");
|
|
3499
|
+
const latest = await fetchLatestVersion();
|
|
3500
|
+
if (latest === null) {
|
|
3501
|
+
p.outro(red("Could not reach the npm registry. Check your connection and try again."));
|
|
3502
|
+
process.exit(1);
|
|
3503
|
+
return;
|
|
3504
|
+
}
|
|
3505
|
+
if (!isUpdateAvailable(currentVersion, latest)) {
|
|
3506
|
+
p.outro(`Already up-to-date: v${currentVersion}`);
|
|
3507
|
+
return;
|
|
3508
|
+
}
|
|
3509
|
+
p.log.step(`${dim(currentVersion)} → ${green(latest)}`);
|
|
3510
|
+
const cmd = buildUpdateCommand(pm);
|
|
3511
|
+
if (flags?.yes !== true) {
|
|
3512
|
+
const confirmed = await p.confirm({
|
|
3513
|
+
message: `Run \`${cmd}\`?`,
|
|
3514
|
+
initialValue: true
|
|
3515
|
+
});
|
|
3516
|
+
if (p.isCancel(confirmed) || !confirmed) {
|
|
3517
|
+
p.outro("Update cancelled.");
|
|
3518
|
+
return;
|
|
3519
|
+
}
|
|
3520
|
+
}
|
|
3521
|
+
p.log.message(`Running ${cyan(cmd)}...`);
|
|
3522
|
+
if (await runUpdate(pm)) {
|
|
3523
|
+
p.outro(green(`Updated to v${latest}`));
|
|
3524
|
+
return;
|
|
3525
|
+
}
|
|
3526
|
+
p.outro(red("Update failed. See output above."));
|
|
3527
|
+
process.exit(1);
|
|
3528
|
+
}
|
|
3529
|
+
//#endregion
|
|
3375
3530
|
//#region src/cli.ts
|
|
3376
3531
|
const { version } = package_default;
|
|
3377
3532
|
cli({
|
|
@@ -3419,19 +3574,34 @@ cli({
|
|
|
3419
3574
|
default: false
|
|
3420
3575
|
}
|
|
3421
3576
|
},
|
|
3422
|
-
commands: [
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3577
|
+
commands: [
|
|
3578
|
+
command({
|
|
3579
|
+
name: "logs",
|
|
3580
|
+
description: "Show debug logs from the last cmint run",
|
|
3581
|
+
flags: { lines: {
|
|
3582
|
+
type: Number,
|
|
3583
|
+
description: "Number of lines to show from the end",
|
|
3584
|
+
alias: "n"
|
|
3585
|
+
} }
|
|
3586
|
+
}, async (argv) => {
|
|
3587
|
+
await logsCommand(argv.flags);
|
|
3588
|
+
}),
|
|
3589
|
+
command({ name: "config" }, async () => {
|
|
3590
|
+
await configCommand();
|
|
3591
|
+
}),
|
|
3592
|
+
command({
|
|
3593
|
+
name: "update",
|
|
3594
|
+
description: "Update cmint to the latest published version",
|
|
3595
|
+
flags: { yes: {
|
|
3596
|
+
type: Boolean,
|
|
3597
|
+
description: "Skip confirmation prompt",
|
|
3598
|
+
alias: "y",
|
|
3599
|
+
default: false
|
|
3600
|
+
} }
|
|
3601
|
+
}, async (argv) => {
|
|
3602
|
+
await updateCommand(version, argv.flags);
|
|
3603
|
+
})
|
|
3604
|
+
]
|
|
3435
3605
|
}, (argv) => {
|
|
3436
3606
|
writeSessionHeader();
|
|
3437
3607
|
setDebug(argv.flags.debug);
|