mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-01 19:42:03 +00:00
refactor(cli): colocate --help examples in command files (#202)
Move per-command examples from the centralized `help.ts` record into each command file as `export const examples: Example[]`. help.ts now dynamically imports them at --help time. This means adding a new command and its examples happens in one file instead of two, reducing the chance of forgetting examples. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
4bb01fd1b6
commit
5e8ff36675
@@ -87,8 +87,8 @@ Always run both on changed files before committing. The lefthook pre-commit hook
|
||||
When adding a new CLI command:
|
||||
|
||||
1. Define the command in `packages/cli/src/commands/<name>.ts` using `defineCommand` from citty
|
||||
2. Register it in `packages/cli/src/cli.ts` under `subCommands` (lazy-loaded)
|
||||
3. **Add examples to `packages/cli/src/help.ts`** in the `COMMAND_EXAMPLES` record — every command must have `--help` examples
|
||||
2. **Export `examples`** in the same file — `export const examples: Example[] = [...]` (import `Example` from `./_examples.js`). These are displayed by `--help`.
|
||||
3. Register it in `packages/cli/src/cli.ts` under `subCommands` (lazy-loaded)
|
||||
4. Validate by running `npx tsx packages/cli/src/cli.ts <name> --help` and verifying the examples section appears
|
||||
|
||||
## Key Concepts
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Shared type for CLI command examples.
|
||||
* Each command file exports `examples` using this type.
|
||||
* help.ts dynamically imports them at --help time.
|
||||
*/
|
||||
export type Example = [comment: string, command: string];
|
||||
@@ -1,5 +1,12 @@
|
||||
import { defineCommand } from "citty";
|
||||
import type { Example } from "./_examples.js";
|
||||
import { existsSync, statSync } from "node:fs";
|
||||
|
||||
export const examples: Example[] = [
|
||||
["Run benchmarks with default settings (3 runs)", "hyperframes benchmark"],
|
||||
["Run 5 iterations per config", "hyperframes benchmark --runs 5"],
|
||||
["Output results as JSON", "hyperframes benchmark --json"],
|
||||
];
|
||||
import { resolve, join } from "node:path";
|
||||
import { resolveProject } from "../utils/project.js";
|
||||
import { loadProducer } from "../utils/producer.js";
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
import { defineCommand } from "citty";
|
||||
import type { Example } from "./_examples.js";
|
||||
import * as clack from "@clack/prompts";
|
||||
import { c } from "../ui/colors.js";
|
||||
|
||||
export const examples: Example[] = [
|
||||
["Find or download Chrome for rendering", "hyperframes browser ensure"],
|
||||
["Print the Chrome executable path", "hyperframes browser path"],
|
||||
["Remove cached Chrome download", "hyperframes browser clear"],
|
||||
];
|
||||
import { formatBytes } from "../ui/format.js";
|
||||
import {
|
||||
ensureBrowser,
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import { defineCommand } from "citty";
|
||||
import type { Example } from "./_examples.js";
|
||||
import { readFileSync } from "node:fs";
|
||||
|
||||
export const examples: Example[] = [
|
||||
["List compositions in the current project", "hyperframes compositions"],
|
||||
["Output as JSON", "hyperframes compositions --json"],
|
||||
];
|
||||
import { c } from "../ui/colors.js";
|
||||
import { ensureDOMParser } from "../utils/dom.js";
|
||||
import { resolveProject } from "../utils/project.js";
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
import { defineCommand } from "citty";
|
||||
import type { Example } from "./_examples.js";
|
||||
import { readFileSync, existsSync } from "node:fs";
|
||||
|
||||
export const examples: Example[] = [
|
||||
["List all available topics", "hyperframes docs"],
|
||||
["Read about data attributes", "hyperframes docs data-attributes"],
|
||||
["Read about rendering", "hyperframes docs rendering"],
|
||||
["Read about GSAP integration", "hyperframes docs gsap"],
|
||||
];
|
||||
import { resolve, dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { c } from "../ui/colors.js";
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { defineCommand } from "citty";
|
||||
import type { Example } from "./_examples.js";
|
||||
import { execSync } from "node:child_process";
|
||||
|
||||
export const examples: Example[] = [["Check system dependencies", "hyperframes doctor"]];
|
||||
import { freemem, platform } from "node:os";
|
||||
import { c } from "../ui/colors.js";
|
||||
import { findBrowser } from "../browser/manager.js";
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import { defineCommand } from "citty";
|
||||
import type { Example } from "./_examples.js";
|
||||
import { readFileSync, readdirSync, statSync } from "node:fs";
|
||||
|
||||
export const examples: Example[] = [
|
||||
["Show project metadata", "hyperframes info"],
|
||||
["Output as JSON", "hyperframes info --json"],
|
||||
];
|
||||
import { join } from "node:path";
|
||||
import { parseHtml } from "@hyperframes/core";
|
||||
import { c } from "../ui/colors.js";
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
import { defineCommand, runCommand } from "citty";
|
||||
import type { Example } from "./_examples.js";
|
||||
|
||||
export const examples: Example[] = [
|
||||
["Create a project with the interactive wizard", "hyperframes init my-video"],
|
||||
["Pick a starter template", "hyperframes init my-video --template warm-grain"],
|
||||
["Start from an existing video file", "hyperframes init my-video --video clip.mp4"],
|
||||
["Start from an audio file", "hyperframes init my-video --audio track.mp3"],
|
||||
["Non-interactive mode (for CI or AI agents)", "hyperframes init my-video --non-interactive"],
|
||||
];
|
||||
import {
|
||||
existsSync,
|
||||
mkdirSync,
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
import { defineCommand } from "citty";
|
||||
import type { Example } from "./_examples.js";
|
||||
import { c } from "../ui/colors.js";
|
||||
|
||||
export const examples: Example[] = [
|
||||
["Lint the current project", "hyperframes lint"],
|
||||
["Lint a specific directory", "hyperframes lint ./my-video"],
|
||||
["Output findings as JSON", "hyperframes lint --json"],
|
||||
["Include info-level findings", "hyperframes lint --verbose"],
|
||||
];
|
||||
import { formatLintFindings } from "../utils/lintFormat.js";
|
||||
import { lintProject } from "../utils/lintProject.js";
|
||||
import { resolveProject } from "../utils/project.js";
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import { defineCommand } from "citty";
|
||||
import type { Example } from "./_examples.js";
|
||||
import { spawn } from "node:child_process";
|
||||
|
||||
export const examples: Example[] = [
|
||||
["Preview the current project", "hyperframes preview"],
|
||||
["Preview a specific project directory", "hyperframes preview ./my-video"],
|
||||
["Use a custom port", "hyperframes preview --port 8080"],
|
||||
];
|
||||
import { existsSync, lstatSync, symlinkSync, unlinkSync, readlinkSync, mkdirSync } from "node:fs";
|
||||
import { resolve, dirname, basename, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
import { defineCommand } from "citty";
|
||||
import type { Example } from "./_examples.js";
|
||||
import { existsSync, mkdirSync, statSync } from "node:fs";
|
||||
|
||||
export const examples: Example[] = [
|
||||
["Render to MP4", "hyperframes render --output output.mp4"],
|
||||
["Render transparent WebM overlay", "hyperframes render --format webm --output overlay.webm"],
|
||||
["High quality at 60fps", "hyperframes render --fps 60 --quality high --output hd.mp4"],
|
||||
["Deterministic render via Docker", "hyperframes render --docker --output deterministic.mp4"],
|
||||
["Parallel rendering with 4 workers", "hyperframes render --workers 4 --output fast.mp4"],
|
||||
];
|
||||
import { cpus, freemem } from "node:os";
|
||||
import { resolve, dirname, join } from "node:path";
|
||||
import { resolveProject } from "../utils/project.js";
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import { defineCommand } from "citty";
|
||||
import type { Example } from "./_examples.js";
|
||||
import { c } from "../ui/colors.js";
|
||||
|
||||
export const examples: Example[] = [
|
||||
["Check current telemetry status", "hyperframes telemetry status"],
|
||||
["Disable telemetry", "hyperframes telemetry disable"],
|
||||
["Enable telemetry", "hyperframes telemetry enable"],
|
||||
];
|
||||
import { readConfig, writeConfig, CONFIG_PATH } from "../telemetry/config.js";
|
||||
|
||||
function runEnable(): void {
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
import { defineCommand } from "citty";
|
||||
import type { Example } from "./_examples.js";
|
||||
import { existsSync, writeFileSync } from "node:fs";
|
||||
|
||||
export const examples: Example[] = [
|
||||
["Transcribe an audio file", "hyperframes transcribe audio.mp3"],
|
||||
["Transcribe a video file", "hyperframes transcribe video.mp4"],
|
||||
["Use a larger model for better accuracy", "hyperframes transcribe audio.mp3 --model medium.en"],
|
||||
["Set language to filter non-target speech", "hyperframes transcribe audio.mp3 --language en"],
|
||||
["Import an existing SRT file", "hyperframes transcribe subtitles.srt"],
|
||||
["Import an OpenAI Whisper JSON response", "hyperframes transcribe response.json"],
|
||||
];
|
||||
import { resolve, join, extname } from "node:path";
|
||||
import * as clack from "@clack/prompts";
|
||||
import { c } from "../ui/colors.js";
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
import { defineCommand } from "citty";
|
||||
import type { Example } from "./_examples.js";
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
|
||||
export const examples: Example[] = [
|
||||
["Generate speech from text", 'hyperframes tts "Welcome to HyperFrames"'],
|
||||
["Choose a voice", 'hyperframes tts "Hello world" --voice am_adam'],
|
||||
["Save to a specific file", 'hyperframes tts "Intro" --voice bf_emma --output narration.wav'],
|
||||
["Adjust speech speed", 'hyperframes tts "Slow and clear" --speed 0.8'],
|
||||
["Read text from a file", "hyperframes tts script.txt"],
|
||||
["List available voices", "hyperframes tts --list"],
|
||||
];
|
||||
import { resolve, extname } from "node:path";
|
||||
import * as clack from "@clack/prompts";
|
||||
import { c } from "../ui/colors.js";
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
import { defineCommand } from "citty";
|
||||
import type { Example } from "./_examples.js";
|
||||
import * as clack from "@clack/prompts";
|
||||
import { c } from "../ui/colors.js";
|
||||
|
||||
export const examples: Example[] = [
|
||||
["Check for updates interactively", "hyperframes upgrade"],
|
||||
["Check for updates without prompting", "hyperframes upgrade --check"],
|
||||
["Show upgrade commands directly", "hyperframes upgrade --yes"],
|
||||
];
|
||||
import { VERSION } from "../version.js";
|
||||
import { checkForUpdate, withMeta } from "../utils/updateCheck.js";
|
||||
|
||||
|
||||
+20
-84
@@ -62,7 +62,7 @@ const GROUPS: Group[] = [
|
||||
];
|
||||
|
||||
// ── Root-level examples ────────────────────────────────────────────────────
|
||||
type Example = [comment: string, command: string];
|
||||
import type { Example } from "./commands/_examples.js";
|
||||
|
||||
const ROOT_EXAMPLES: Example[] = [
|
||||
["Create a new project", "hyperframes init my-video"],
|
||||
@@ -73,93 +73,26 @@ const ROOT_EXAMPLES: Example[] = [
|
||||
["Check system dependencies", "hyperframes doctor"],
|
||||
];
|
||||
|
||||
// ── Per-command examples (comment + command style: comment + command) ────────────────
|
||||
const COMMAND_EXAMPLES: Record<string, Example[]> = {
|
||||
init: [
|
||||
["Create a project with the interactive wizard", "hyperframes init my-video"],
|
||||
["Pick a starter template", "hyperframes init my-video --template warm-grain"],
|
||||
["Start from an existing video file", "hyperframes init my-video --video clip.mp4"],
|
||||
["Start from an audio file", "hyperframes init my-video --audio track.mp3"],
|
||||
["Non-interactive mode (for CI or AI agents)", "hyperframes init my-video --non-interactive"],
|
||||
],
|
||||
preview: [
|
||||
["Preview the current project", "hyperframes preview"],
|
||||
["Preview a specific project directory", "hyperframes preview ./my-video"],
|
||||
["Use a custom port", "hyperframes preview --port 8080"],
|
||||
],
|
||||
render: [
|
||||
["Render to MP4", "hyperframes render --output output.mp4"],
|
||||
["Render transparent WebM overlay", "hyperframes render --format webm --output overlay.webm"],
|
||||
["High quality at 60fps", "hyperframes render --fps 60 --quality high --output hd.mp4"],
|
||||
["Deterministic render via Docker", "hyperframes render --docker --output deterministic.mp4"],
|
||||
["Parallel rendering with 4 workers", "hyperframes render --workers 4 --output fast.mp4"],
|
||||
],
|
||||
lint: [
|
||||
["Lint the current project", "hyperframes lint"],
|
||||
["Lint a specific directory", "hyperframes lint ./my-video"],
|
||||
["Output findings as JSON", "hyperframes lint --json"],
|
||||
["Include info-level findings", "hyperframes lint --verbose"],
|
||||
],
|
||||
info: [
|
||||
["Show project metadata", "hyperframes info"],
|
||||
["Output as JSON", "hyperframes info --json"],
|
||||
],
|
||||
compositions: [
|
||||
["List compositions in the current project", "hyperframes compositions"],
|
||||
["Output as JSON", "hyperframes compositions --json"],
|
||||
],
|
||||
benchmark: [
|
||||
["Run benchmarks with default settings (3 runs)", "hyperframes benchmark"],
|
||||
["Run 5 iterations per config", "hyperframes benchmark --runs 5"],
|
||||
["Output results as JSON", "hyperframes benchmark --json"],
|
||||
],
|
||||
browser: [
|
||||
["Find or download Chrome for rendering", "hyperframes browser ensure"],
|
||||
["Print the Chrome executable path", "hyperframes browser path"],
|
||||
["Remove cached Chrome download", "hyperframes browser clear"],
|
||||
],
|
||||
// ── Per-command examples loaded from command files ────────────────────────
|
||||
// Each command file exports `examples: Example[]`. This function dynamically
|
||||
// imports them so examples live next to the command they document.
|
||||
async function loadExamples(name: string): Promise<Example[] | undefined> {
|
||||
try {
|
||||
const mod = await import(`./commands/${name}.js`);
|
||||
return mod.examples;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// Commands without their own file (e.g. listed in help but not yet a real command)
|
||||
const STATIC_EXAMPLES: Record<string, Example[]> = {
|
||||
skills: [
|
||||
["Install skills to all supported AI tools", "hyperframes skills"],
|
||||
["Install to Claude Code only", "hyperframes skills --claude"],
|
||||
["Install to Cursor (project-level)", "hyperframes skills --cursor"],
|
||||
["Install to specific tools", "hyperframes skills --claude --gemini"],
|
||||
],
|
||||
tts: [
|
||||
["Generate speech from text", 'hyperframes tts "Welcome to HyperFrames"'],
|
||||
["Choose a voice", 'hyperframes tts "Hello world" --voice am_adam'],
|
||||
["Save to a specific file", 'hyperframes tts "Intro" --voice bf_emma --output narration.wav'],
|
||||
["Adjust speech speed", 'hyperframes tts "Slow and clear" --speed 0.8'],
|
||||
["Read text from a file", "hyperframes tts script.txt"],
|
||||
["List available voices", "hyperframes tts --list"],
|
||||
],
|
||||
transcribe: [
|
||||
["Transcribe an audio file", "hyperframes transcribe audio.mp3"],
|
||||
["Transcribe a video file", "hyperframes transcribe video.mp4"],
|
||||
[
|
||||
"Use a larger model for better accuracy",
|
||||
"hyperframes transcribe audio.mp3 --model medium.en",
|
||||
],
|
||||
["Set language to filter non-target speech", "hyperframes transcribe audio.mp3 --language en"],
|
||||
["Import an existing SRT file", "hyperframes transcribe subtitles.srt"],
|
||||
["Import an OpenAI Whisper JSON response", "hyperframes transcribe response.json"],
|
||||
],
|
||||
docs: [
|
||||
["List all available topics", "hyperframes docs"],
|
||||
["Read about data attributes", "hyperframes docs data-attributes"],
|
||||
["Read about rendering", "hyperframes docs rendering"],
|
||||
["Read about GSAP integration", "hyperframes docs gsap"],
|
||||
],
|
||||
doctor: [["Check system dependencies", "hyperframes doctor"]],
|
||||
upgrade: [
|
||||
["Check for updates interactively", "hyperframes upgrade"],
|
||||
["Check for updates without prompting", "hyperframes upgrade --check"],
|
||||
["Show upgrade commands directly", "hyperframes upgrade --yes"],
|
||||
],
|
||||
telemetry: [
|
||||
["Check current telemetry status", "hyperframes telemetry status"],
|
||||
["Disable telemetry", "hyperframes telemetry disable"],
|
||||
["Enable telemetry", "hyperframes telemetry enable"],
|
||||
],
|
||||
};
|
||||
|
||||
// ── Render root help ───────────────────────────────────────────────────────
|
||||
@@ -218,7 +151,10 @@ export async function showUsage(cmd: CommandDef, parent?: CommandDef): Promise<v
|
||||
console.log(usage + "\n");
|
||||
|
||||
const name = meta?.name;
|
||||
if (name && COMMAND_EXAMPLES[name]) {
|
||||
console.log(formatExamples(COMMAND_EXAMPLES[name]) + "\n");
|
||||
if (name) {
|
||||
const examples = STATIC_EXAMPLES[name] ?? (await loadExamples(name));
|
||||
if (examples) {
|
||||
console.log(formatExamples(examples) + "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user