mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 04:38:33 +00:00
refactor(cli): add stderr diagnostics logger + oxlint guard (#2551)
This commit is contained in:
@@ -19,6 +19,7 @@ import { extractDesignStyles } from "./designStyleExtractor.js";
|
||||
import { downloadAssets, downloadAndRewriteFonts } from "./assetDownloader.js";
|
||||
import { extractFontMetadata } from "./fontMetadataExtractor.js";
|
||||
import { normalizeErrorMessage } from "../utils/errorMessage.js";
|
||||
import { diag } from "../ui/diagnostics.js";
|
||||
// briefGenerator.ts, visual-style, capture-summary removed — DESIGN.md replaces them
|
||||
import {
|
||||
setupAnimationCapture,
|
||||
@@ -474,17 +475,17 @@ export async function captureWebsite(
|
||||
const summary = fontsManifest.families
|
||||
.map((f) => `${f.family}${f.variable ? " (variable)" : ""} × ${f.fileCount}`)
|
||||
.join(", ");
|
||||
// stderr: `capture --json` writes its envelope to stdout; this progress
|
||||
// note must not corrupt it (matches the sibling console.warn below).
|
||||
console.warn(`Font metadata extracted: ${summary}`);
|
||||
// stderr (via diag): `capture --json` writes its envelope to stdout, so
|
||||
// these progress/advisory lines must not land there.
|
||||
diag.notice(`Font metadata extracted: ${summary}`);
|
||||
if (fontsManifest.unidentified.length > 0) {
|
||||
console.warn(
|
||||
diag.warn(
|
||||
` ${fontsManifest.unidentified.length} font file(s) could not be identified — DESIGN.md should flag these explicitly.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn("Font metadata extraction failed (non-fatal):", normalizeErrorMessage(err));
|
||||
diag.warn("Font metadata extraction failed (non-fatal):", normalizeErrorMessage(err));
|
||||
}
|
||||
|
||||
// Save animation catalog — lean version for the agent (not 745 raw CSS declarations)
|
||||
|
||||
@@ -2,6 +2,7 @@ import { defineCommand } from "citty";
|
||||
import { execFileSync, spawn } from "node:child_process";
|
||||
import * as clack from "@clack/prompts";
|
||||
import { c } from "../ui/colors.js";
|
||||
import { diag } from "../ui/diagnostics.js";
|
||||
import { buildNpxCommand } from "../utils/npxCommand.js";
|
||||
import { withMeta } from "../utils/updateCheck.js";
|
||||
import {
|
||||
@@ -176,9 +177,9 @@ function mirrorToInstalledAgents(): void {
|
||||
const { mirrored } = mirrorGlobalSkills({ skills: names });
|
||||
const n = mirrored.length;
|
||||
if (n > 0) {
|
||||
// stderr: reachable from `skills update --json` (via installSkills) before
|
||||
// the JSON envelope is written to stdout.
|
||||
console.error(
|
||||
// stderr (via diag): reachable from `skills update --json` (via installSkills)
|
||||
// before the JSON envelope is written to stdout.
|
||||
diag.notice(
|
||||
c.dim(`Linked skills into ${n} other agent ${n === 1 ? "directory" : "directories"}.`),
|
||||
);
|
||||
}
|
||||
@@ -251,17 +252,18 @@ async function installSkills(
|
||||
|
||||
if (!skillsToolingReady(opts.strict ?? false)) return;
|
||||
|
||||
// stderr: installSkills runs on the `skills update --json` path before its JSON
|
||||
// envelope is written to stdout — progress here must not corrupt that output.
|
||||
// stderr (via diag): installSkills runs on the `skills update --json` path before
|
||||
// its JSON envelope is written to stdout — progress here must not corrupt it.
|
||||
for (const source of SOURCES) {
|
||||
console.error();
|
||||
console.error(c.bold(`Installing ${source.name} skills...`));
|
||||
console.error();
|
||||
diag.notice();
|
||||
diag.notice(c.bold(`Installing ${source.name} skills...`));
|
||||
diag.notice();
|
||||
try {
|
||||
await runSkillsAdd(source.url, safeSelection, opts);
|
||||
} catch (err) {
|
||||
if (opts.strict) throw err instanceof Error ? err : new Error(String(err));
|
||||
console.error(c.dim(`${source.name} skills skipped`));
|
||||
// warn, not notice: this is a non-fatal skip after a caught install error.
|
||||
diag.warn(c.dim(`${source.name} skills skipped`));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { randomUUID } from "node:crypto";
|
||||
import { readConfig, writeConfig } from "./config.js";
|
||||
import { VERSION } from "../version.js";
|
||||
import { c } from "../ui/colors.js";
|
||||
import { diag } from "../ui/diagnostics.js";
|
||||
import { isDevMode } from "../utils/env.js";
|
||||
import { getSystemMeta } from "./system.js";
|
||||
|
||||
@@ -219,18 +220,18 @@ export function showTelemetryNotice(): boolean {
|
||||
config.telemetryNoticeShown = true;
|
||||
writeConfig(config);
|
||||
|
||||
// stderr, not stdout: this first-run disclosure is not gated by --json (the
|
||||
// guard in cli.ts filters by command only), so a stdout banner would corrupt
|
||||
// the JSON envelope of the very first `check --json` / `info --json` etc.
|
||||
console.error();
|
||||
console.error(` ${c.dim("Hyperframes collects anonymous usage data to improve the tool.")}`);
|
||||
console.error(` ${c.dim("File paths and composition content are never collected.")}`);
|
||||
console.error(
|
||||
// stderr (via diag), not stdout: this first-run disclosure is not gated by
|
||||
// --json (the guard in cli.ts filters by command only), so a stdout banner
|
||||
// would corrupt the JSON envelope of the very first `check --json` etc.
|
||||
diag.notice();
|
||||
diag.notice(` ${c.dim("Hyperframes collects anonymous usage data to improve the tool.")}`);
|
||||
diag.notice(` ${c.dim("File paths and composition content are never collected.")}`);
|
||||
diag.notice(
|
||||
` ${c.dim("If you sign in to HeyGen, your account (email, or username) is linked to your usage.")}`,
|
||||
);
|
||||
console.error();
|
||||
console.error(` ${c.dim("Disable anytime:")} ${c.accent("hyperframes telemetry disable")}`);
|
||||
console.error();
|
||||
diag.notice();
|
||||
diag.notice(` ${c.dim("Disable anytime:")} ${c.accent("hyperframes telemetry disable")}`);
|
||||
diag.notice();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* CLI diagnostics logger.
|
||||
*
|
||||
* Diagnostics (notices, progress, warnings, errors) go to **stderr**. stdout is
|
||||
* reserved for a command's machine-readable payload — most importantly `--json`
|
||||
* output — and its primary human result. A diagnostic written to stdout corrupts
|
||||
* that payload for any consumer that parses it (see #2520 / #2522).
|
||||
*
|
||||
* This mirrors the producer's `ProducerLogger` contract for the CLI, but without
|
||||
* `[LEVEL]` prefixes so user-facing notices keep their own formatting. Route all
|
||||
* diagnostic output through `diag` (or `console.error` / `console.warn` directly)
|
||||
* — never `console.log` / `console.info` for diagnostics.
|
||||
*/
|
||||
export const diag = {
|
||||
/** A user-facing notice or progress line (e.g. "Installing … skills…"). */
|
||||
notice(...args: unknown[]): void {
|
||||
console.error(...args);
|
||||
},
|
||||
/** A warning the user should see but that isn't fatal. */
|
||||
warn(...args: unknown[]): void {
|
||||
console.warn(...args);
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user