perf(cli): load each command's code only when that command runs (#4656)

* perf(cli): load each command's code only when that command runs

* fix(cli): honest bench percentiles, and no unused registry entry in the split build

* fix(cli): the startup bench is a package script and stops on a failed run

* style(cli): format the bench's failed-run check

* fix(cli): a notice module that fails to load cannot fail the command
This commit is contained in:
Miguel Ángel
2026-09-29 00:07:28 +00:00
committed by GitHub
parent e25bc3a806
commit e07ea1fc70
5 changed files with 72 additions and 20 deletions
+2 -1
View File
@@ -27,7 +27,8 @@
"build:copy": "node scripts/build-copy.mjs",
"typecheck": "tsc --noEmit",
"gen:skills-manifest": "tsx scripts/gen-skills-manifest.ts",
"gen:agent-dirs": "tsx scripts/sync-agent-dirs.ts"
"gen:agent-dirs": "tsx scripts/sync-agent-dirs.ts",
"bench:startup": "node scripts/bench-startup.mjs"
},
"dependencies": {
"@hono/node-server": "^2.0.5",
+52
View File
@@ -0,0 +1,52 @@
#!/usr/bin/env node
// Times whole CLI runs (spawn to exit) and prints a markdown table of median and p90.
// Usage: node scripts/bench-startup.mjs <project-dir> [--runs 10] [--cli bin/hyperframes.mjs] [--no-telemetry]
// Runs with CI=1 (no update check); telemetry stays on unless --no-telemetry, so each run sends real events.
import { spawnSync } from "node:child_process";
import { resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { parseArgs } from "node:util";
const { values, positionals } = parseArgs({
allowPositionals: true,
options: {
runs: { type: "string", default: "10" },
cli: { type: "string" },
"no-telemetry": { type: "boolean", default: false },
},
});
const project = resolve(positionals[0] ?? ".");
const runs = Number(values.runs);
if (!Number.isInteger(runs) || runs < 1) throw new Error("--runs must be a positive integer");
const cli = resolve(
values.cli ?? fileURLToPath(new URL("../bin/hyperframes.mjs", import.meta.url)),
);
const env = { ...process.env, CI: "1" };
if (values["no-telemetry"]) env.HYPERFRAMES_NO_TELEMETRY = "1";
const commands = [["--help"], ["lint", "--help"], ["lint"], ["compositions"], ["info"]];
// Nearest rank: p90 of 10 runs is the 9th fastest, not the slowest.
const percentile = (sorted, p) => sorted[Math.ceil(p * sorted.length) - 1];
function timeRun(command) {
const start = process.hrtime.bigint();
const result = spawnSync(process.execPath, [cli, ...command], {
cwd: project,
env,
stdio: "ignore",
});
const seconds = Number(process.hrtime.bigint() - start) / 1e9;
// A failed run times nothing worth reporting.
if (result.status !== 0)
throw result.error ?? new Error(`${command.join(" ")} exited ${result.status}`);
return seconds;
}
console.log(`| command | median (s) | p90 (s) |\n|---|---|---|`);
for (const command of commands) {
timeRun(command); // warm-up: fills the OS file cache, not counted
const times = Array.from({ length: runs }, () => timeRun(command)).sort((a, b) => a - b);
console.log(
`| ${command.join(" ")} | ${percentile(times, 0.5).toFixed(2)} | ${percentile(times, 0.9).toFixed(2)} |`,
);
}
-4
View File
@@ -102,10 +102,6 @@ async function main() {
join(mediaEngine, "resolve.mjs"),
join(DIST, "skills", "media-use", "scripts", "resolve.mjs"),
);
mkdirSync(join(DIST, "skills", "registry"), { recursive: true });
copyDirContents(join(DIST, "registry"), join(DIST, "skills", "registry"));
mkdirSync(join(DIST, "skills", "media-use", "registry"), { recursive: true });
copyDirContents(join(DIST, "registry"), join(DIST, "skills", "media-use", "registry"));
const dockerfile = join(CLI_ROOT, "src", "docker", "Dockerfile.render");
if (existsSync(dockerfile)) {
+17 -13
View File
@@ -26,7 +26,7 @@ for (const stream of [process.stdout, process.stderr]) {
// The shaderTransitionWorkerPool lives in the producer package and resolves
// its worker entry by probing for a sibling `.js` file next to
// `import.meta.url`. When this CLI is bundled by tsup, the producer code is
// inlined into `cli.js`, but `import.meta.url` resolves to the producer's
// bundled into chunks beside cli.js, but `import.meta.url` resolves to the producer's
// own dist path (NOT cli.js) on some module-graph layouts — so the sibling
// probe lands in a directory that does not contain the bundled worker.
// We emit the worker entry next to cli.js (see tsup.config.ts) and tell
@@ -279,20 +279,24 @@ if (
// we don't over-print.
import("./utils/autoUpdate.js").then((mod) => mod.reportCompletedUpdate()).catch(() => {});
import("./utils/updateCheck.js").then(async (mod) => {
_printUpdateNotice = mod.printUpdateNotice;
_printStalePinNotice = mod.printStalePinNotice;
const result = mod.cachedUpdateCheck();
if (result.updateAvailable) {
const auto = await import("./utils/autoUpdate.js").catch(() => null);
auto?.scheduleBackgroundInstall(result.latest, result.current);
}
});
import("./utils/updateCheck.js")
.then(async (mod) => {
_printUpdateNotice = mod.printUpdateNotice;
_printStalePinNotice = mod.printStalePinNotice;
const result = mod.cachedUpdateCheck();
if (result.updateAvailable) {
const auto = await import("./utils/autoUpdate.js").catch(() => null);
auto?.scheduleBackgroundInstall(result.latest, result.current);
}
})
.catch(() => {});
// Skills freshness nudge — same gating as the CLI self-update notice.
import("./utils/skillsUpdateCheck.js").then((mod) => {
_printSkillsUpdateNotice = mod.printSkillsUpdateNotice;
});
import("./utils/skillsUpdateCheck.js")
.then((mod) => {
_printSkillsUpdateNotice = mod.printSkillsUpdateNotice;
})
.catch(() => {});
// The notices read the caches; a detached child refreshes them for the next run.
import("./utils/backgroundChecks.js").then((mod) => mod.launchBackgroundChecks()).catch(() => {});
+1 -2
View File
@@ -19,14 +19,13 @@ export default defineConfig({
backgroundChecksWorker: "src/backgroundChecksWorker.ts",
sherpaWorker: "src/whisper/sherpaWorker.ts",
shaderTransitionWorker: "../producer/src/services/shaderTransitionWorker.ts",
"registry/localSemantic": "src/registry/localSemantic.ts",
},
format: ["esm"],
outDir: "dist",
target: "node22",
platform: "node",
bundle: true,
splitting: false,
splitting: true,
sourcemap: false,
clean: true,
banner: {