Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .oxlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,19 @@
"packages/producer/tests/**/src/vendor/**",
"registry/blocks/**/lib/*.iife.js",
"chrome/"
],
"overrides": [
{
"files": [
"packages/cli/src/capture/**/*.ts",
"packages/cli/src/registry/**/*.ts",
"packages/cli/src/templates/**/*.ts",
"packages/cli/src/telemetry/**/*.ts"
],
"excludeFiles": ["**/*.test.ts", "**/*.test.tsx", "packages/cli/src/telemetry/feedback.ts"],
"rules": {
"no-console": ["error", { "allow": ["error", "warn"] }]
}
}
]
}
11 changes: 6 additions & 5 deletions packages/cli/src/capture/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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)
Expand Down
20 changes: 11 additions & 9 deletions packages/cli/src/commands/skills.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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"}.`),
);
}
Expand Down Expand Up @@ -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`));
}
}

Expand Down
21 changes: 11 additions & 10 deletions packages/cli/src/telemetry/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -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;
}
23 changes: 23 additions & 0 deletions packages/cli/src/ui/diagnostics.ts
Original file line number Diff line number Diff line change
@@ -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);
},
};
Loading