fix(cli): replace process.exit(1) with process.exitCode = 1

process.exit() forcefully kills the process, preventing libuv from
cleanly closing HTTP connections (undici). On Windows this triggers
an assertion failure: UV_HANDLE_CLOSING.

Using process.exitCode = 1 lets Node.js exit gracefully after the
event loop drains, eliminating the assertion error while still
returning exit code 1.
This commit is contained in:
chenbaowang 2026-04-21 10:54:50 +08:00
parent 8815c1596c
commit d96ef86fac
23 changed files with 49 additions and 49 deletions

View file

@ -33,7 +33,7 @@ export function registerArchive(program: Command) {
success(`Archived ${skillSlug}`);
} catch (e: any) {
error(`Failed: ${e.message}`);
process.exit(1);
process.exitCode = 1;
}
});
}

View file

@ -34,7 +34,7 @@ export function registerDelete(program: Command) {
success(`Deleted ${skillSlug} from ${namespace}`);
} catch (e: any) {
error(`Failed: ${e.message}`);
process.exit(1);
process.exitCode = 1;
}
});
}

View file

@ -45,7 +45,7 @@ export function registerDownload(program: Command) {
const location = response.headers.location;
if (!location) {
spinner.fail(`Redirect response has no Location header`);
process.exit(1);
process.exitCode = 1;
}
response = await request(location as string, { method: "GET" });
}
@ -53,7 +53,7 @@ export function registerDownload(program: Command) {
if (statusCode >= 400) {
spinner.fail(`Download failed: HTTP ${statusCode}`);
process.exit(1);
process.exitCode = 1;
}
const outPath = resolve(outputDir, `${skillSlug}.zip`);
@ -63,7 +63,7 @@ export function registerDownload(program: Command) {
spinner.succeed(`Downloaded ${skillSlug} to ${outPath}`);
} catch (e: any) {
error(`Download failed: ${e.message}`);
process.exit(1);
process.exitCode = 1;
}
});
}

View file

@ -42,7 +42,7 @@ export function registerHide(program: Command) {
success(`Hidden ${skillSlug}`);
} catch (e: any) {
error(`Failed: ${e.message}`);
process.exit(1);
process.exitCode = 1;
}
});
@ -82,7 +82,7 @@ export function registerHide(program: Command) {
success(`Unhidden ${skillSlug}`);
} catch (e: any) {
error(`Failed: ${e.message}`);
process.exit(1);
process.exitCode = 1;
}
});
}

View file

@ -17,7 +17,7 @@ export function registerInit(program: Command) {
const skillMd = join(dir, "SKILL.md");
if (existsSync(skillMd)) {
error("SKILL.md already exists");
process.exit(1);
process.exitCode = 1;
}
const slug = name || "my-skill";

View file

@ -94,7 +94,7 @@ export function registerInspect(program: Command) {
if (!namespaces || namespaces.length === 0) {
error("No namespaces found. You may need to log in.");
process.exit(1);
process.exitCode = 1;
}
const searchPromises = namespaces.map(async (ns) => {
@ -116,7 +116,7 @@ export function registerInspect(program: Command) {
if (namespaces.length > 1) {
dim(`Tried namespaces: ${namespaces.map((n) => n.slug).join(", ")}`);
}
process.exit(1);
process.exitCode = 1;
}
if (isJson) {

View file

@ -233,7 +233,7 @@ export function registerInstall(program: Command) {
}
} catch (e: any) {
spinner.fail(e.message);
process.exit(1);
process.exitCode = 1;
}
});
}
@ -277,7 +277,7 @@ async function installFromRegistry(
if (uniqueResults.length === 0) {
spinner.fail(`Skill not found: ${actualSlug}`);
process.exit(1);
process.exitCode = 1;
}
if (uniqueResults.length === 1) {
@ -373,7 +373,7 @@ async function installFromRegistry(
if (!location) {
spinner.fail(`Redirect response has no Location header`);
await rm(tmpDir, { recursive: true, force: true });
process.exit(1);
process.exitCode = 1;
}
response = await request(location as string, { method: "GET" });
}
@ -382,7 +382,7 @@ async function installFromRegistry(
if (statusCode >= 400) {
spinner.fail(`Skill not found: ${ns}/${actualSlug}`);
await rm(tmpDir, { recursive: true, force: true });
process.exit(1);
process.exitCode = 1;
}
const fileStream = createWriteStream(zipPath);
@ -398,7 +398,7 @@ async function installFromRegistry(
const skills = discoverSkills(extractDir);
if (skills.length === 0) {
spinner.fail("No SKILL.md found in package");
process.exit(1);
process.exitCode = 1;
}
spinner.succeed(`Found ${skills.length} skill(s) in ${ns}/${actualSlug}`);
@ -639,7 +639,7 @@ async function installFromGit(
if (skills.length === 0) {
spinner.fail("No skills found. Ensure the directory contains SKILL.md files.");
process.exit(1);
process.exitCode = 1;
}
spinner.succeed(`Found ${skills.length} skill(s)`);
@ -663,7 +663,7 @@ async function installFromGit(
if (selectedSkills.length === 0) {
error(`No matching skills for: ${skillNames.join(", ")}`);
info("Available: " + skills.map((s) => s.name).join(", "));
process.exit(1);
process.exitCode = 1;
}
}
} else if (!opts.yes && skills.length > 1) {

View file

@ -28,7 +28,7 @@ export function registerLogin(program: Command) {
success(`Authenticated as ${resp.user.displayName} (@${resp.user.handle})`);
} catch (e: any) {
error(`Authentication failed: ${e.message}`);
process.exit(1);
process.exitCode = 1;
}
});
}

View file

@ -53,7 +53,7 @@ export function registerMe(program: Command) {
}
} catch (e: any) {
error(`Failed: ${e.message}`);
process.exit(1);
process.exitCode = 1;
}
});
@ -83,7 +83,7 @@ export function registerMe(program: Command) {
}
} catch (e: any) {
error(`Failed: ${e.message}`);
process.exit(1);
process.exitCode = 1;
}
});
}

View file

@ -29,7 +29,7 @@ export function registerNamespaces(program: Command) {
}
} catch (e: any) {
error(`Failed to list namespaces: ${e.message}`);
process.exit(1);
process.exitCode = 1;
}
});
}

View file

@ -40,7 +40,7 @@ export function registerNotifications(program: Command) {
}
} catch (e: any) {
error(`Failed: ${e.message}`);
process.exit(1);
process.exitCode = 1;
}
});
@ -56,7 +56,7 @@ export function registerNotifications(program: Command) {
success(`Marked notification ${id} as read`);
} catch (e: any) {
error(`Failed: ${e.message}`);
process.exit(1);
process.exitCode = 1;
}
});
@ -72,7 +72,7 @@ export function registerNotifications(program: Command) {
success("All notifications marked as read");
} catch (e: any) {
error(`Failed: ${e.message}`);
process.exit(1);
process.exitCode = 1;
}
});
}

View file

@ -25,7 +25,7 @@ export function registerPublish(program: Command) {
const folderStat = await stat(folder).catch(() => null);
if (!folderStat || !folderStat.isDirectory()) {
error("Path must be a directory containing SKILL.md");
process.exit(1);
process.exitCode = 1;
}
const slug = opts.slug || basename(folder);
@ -40,7 +40,7 @@ export function registerPublish(program: Command) {
const isValidVersion = semver.valid(version) || /^\d{8}\.\d+$/.test(version);
if (!isValidVersion) {
error("--skill-version must be a valid semver (e.g. 1.0.0) or timestamp (e.g. 20260414.123045)");
process.exit(1);
process.exitCode = 1;
}
const namespace = opts.namespace || "global";
@ -58,7 +58,7 @@ export function registerPublish(program: Command) {
const skillMdStat = await stat(skillMdPath).catch(() => null);
if (!skillMdStat) {
spinner.fail("SKILL.md not found in directory");
process.exit(1);
process.exitCode = 1;
}
const skillMdContent = await readFile(skillMdPath, "utf-8");
@ -92,7 +92,7 @@ export function registerPublish(program: Command) {
}
} catch (e: any) {
error(`Publish failed: ${e.message}`);
process.exit(1);
process.exitCode = 1;
}
});
}

View file

@ -32,7 +32,7 @@ export function registerRating(program: Command) {
}
} catch (e: any) {
error(`Failed: ${e.message}`);
process.exit(1);
process.exitCode = 1;
}
});
}
@ -45,7 +45,7 @@ export function registerRate(program: Command) {
const score = parseInt(scoreStr, 10);
if (isNaN(score) || score < 1 || score > 5) {
error("Score must be between 1 and 5");
process.exit(1);
process.exitCode = 1;
}
try {
@ -65,7 +65,7 @@ export function registerRate(program: Command) {
success(`Rated ${skillSlug}: ${"★".repeat(score)}${"☆".repeat(5 - score)}`);
} catch (e: any) {
error(`Failed: ${e.message}`);
process.exit(1);
process.exitCode = 1;
}
});
}

View file

@ -34,7 +34,7 @@ export function registerReport(program: Command) {
success(`Report submitted for ${skillSlug}`);
} catch (e: any) {
error(`Failed: ${e.message}`);
process.exit(1);
process.exitCode = 1;
}
});
}

View file

@ -70,7 +70,7 @@ export function registerResolve(program: Command) {
}
error(`Version ${specifiedVersion} not found for ${namespace}/${skillSlug}`);
error(`Please check if the version number is correct.`);
process.exit(1);
process.exitCode = 1;
}
const results = await searchSkills(client, skillSlug, 50);
@ -86,7 +86,7 @@ export function registerResolve(program: Command) {
if (uniqueResults.length === 0) {
error(`Skill not found: ${skillSlug}`);
process.exit(1);
process.exitCode = 1;
}
const resolvePromises = uniqueResults.map(async (r) => ({
@ -99,7 +99,7 @@ export function registerResolve(program: Command) {
if (matches.length === 0) {
error(`Version ${specifiedVersion} not found for ${skillSlug}`);
error(`Please check if the version number is correct.`);
process.exit(1);
process.exitCode = 1;
}
if (matches.length === 1) {
@ -114,7 +114,7 @@ export function registerResolve(program: Command) {
console.log(` ${m.namespace}/${m.name}`);
}
dim(`\nUse: resolve <namespace>/<skill> --skill-version ${specifiedVersion}`);
process.exit(1);
process.exitCode = 1;
}
// Case 2: No version specified (original behavior)
@ -133,7 +133,7 @@ export function registerResolve(program: Command) {
if (uniqueResults.length === 0) {
error(`Skill not found: ${skillSlug}`);
process.exit(1);
process.exitCode = 1;
}
if (uniqueResults.length === 1) {
@ -163,7 +163,7 @@ export function registerResolve(program: Command) {
printResolveResult(result);
} catch (e: any) {
error(`Failed: ${e.message}`);
process.exit(1);
process.exitCode = 1;
}
});
}

View file

@ -37,7 +37,7 @@ export function registerReviews(program: Command) {
}
} catch (e: any) {
error(`Failed: ${e.message}`);
process.exit(1);
process.exitCode = 1;
}
});
}

View file

@ -48,7 +48,7 @@ export function registerSearch(program: Command) {
}
} catch (e: any) {
error(`Search failed: ${e.message}`);
process.exit(1);
process.exitCode = 1;
}
});
}

View file

@ -31,7 +31,7 @@ export function registerStar(program: Command) {
}
} catch (e: any) {
error(`Failed: ${e.message}`);
process.exit(1);
process.exitCode = 1;
}
});
}

View file

@ -31,7 +31,7 @@ export function registerSync(program: Command) {
if (!existsSync(scanPath)) {
error(`Directory not found: ${scanPath}`);
process.exit(1);
process.exitCode = 1;
}
try {
@ -139,7 +139,7 @@ export function registerSync(program: Command) {
console.log("");
} catch (e: any) {
error(`Sync failed: ${e.message}`);
process.exit(1);
process.exitCode = 1;
}
});
}

View file

@ -32,7 +32,7 @@ export function registerTransfer(program: Command) {
success(`Ownership of ${namespace} transferred to ${newOwnerId}`);
} catch (e: any) {
error(`Failed: ${e.message}`);
process.exit(1);
process.exitCode = 1;
}
});
}

View file

@ -22,7 +22,7 @@ export function registerUpdate(program: Command) {
if (!existsSync(lockPath)) {
error("No skillhub.lock found. Have you installed any skills?");
process.exit(1);
process.exitCode = 1;
}
const lockedSkills = await getAllLockedSkills();
@ -30,7 +30,7 @@ export function registerUpdate(program: Command) {
if (allSkillNames.length === 0) {
error("No skills in lock file.");
process.exit(1);
process.exitCode = 1;
}
let skillsToUpdate: string[] = [];

View file

@ -59,7 +59,7 @@ export function registerVersions(program: Command) {
if (uniqueResults.length === 0) {
error(`Skill not found: ${skillSlug}`);
process.exit(1);
process.exitCode = 1;
}
if (uniqueResults.length === 1) {
@ -95,7 +95,7 @@ export function registerVersions(program: Command) {
}
} catch (e: any) {
error(`Failed: ${e.message}`);
process.exit(1);
process.exitCode = 1;
}
});
}

View file

@ -24,7 +24,7 @@ export function registerWhoami(program: Command) {
}
} catch (e: any) {
error(e.message);
process.exit(1);
process.exitCode = 1;
}
});
}