mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-01 19:42:03 +00:00
feat(cli): add Windsurf, Cline, Roo, Trae skill install targets
- Add project-level skill targets: Windsurf, Cline, Roo Code, Trae - Split install logic into global vs project-level paths - Fix lint false positive for timed tags with data-composition-id - Update CLAUDE.md with new install examples and supported targets 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
efb09e33ba
commit
6956531ecc
Submodule
+1
Submodule .claude/worktrees/agent-a692178b added at 1e4c101fb4
@@ -39,11 +39,15 @@ The skills encode HyperFrames-specific patterns (e.g., required `class="clip"` o
|
||||
### Installing skills
|
||||
|
||||
```bash
|
||||
npx hyperframes skills # install all to Claude, Gemini, Codex
|
||||
npx hyperframes skills --claude # Claude Code only
|
||||
npx hyperframes skills # install to Claude, Gemini, Codex (global)
|
||||
npx hyperframes skills --claude # Claude Code only
|
||||
npx hyperframes skills --windsurf # Windsurf (project-level)
|
||||
npx hyperframes skills --cursor --cline --roo --trae # multiple project targets
|
||||
npx skills add greensock/gsap-skills # alternative: via skills CLI
|
||||
```
|
||||
|
||||
Supported targets: Claude Code, Gemini CLI, Codex CLI (global, enabled by default), Cursor, Windsurf, Cline, Roo Code, Trae (project-level, opt-in via flag).
|
||||
|
||||
## Project Overview
|
||||
|
||||
Open-source video rendering framework: write HTML, render video.
|
||||
|
||||
@@ -20,7 +20,7 @@ function execFileAsync(
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Target CLI tools — each has a global skills directory
|
||||
// Target CLI tools — each has a skills directory (global or project-level)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
interface Target {
|
||||
@@ -29,15 +29,19 @@ interface Target {
|
||||
/** Agent name for `npx skills add -a <agent>` */
|
||||
skillsAgent: string;
|
||||
dir: string;
|
||||
/** Global targets install to ~/.<agent>/skills/; project targets install to ./<agent>/skills/ */
|
||||
isGlobal: boolean;
|
||||
defaultEnabled: boolean;
|
||||
}
|
||||
|
||||
const TARGETS: Target[] = [
|
||||
// -- Global targets (enabled by default) --
|
||||
{
|
||||
name: "Claude Code",
|
||||
flag: "claude",
|
||||
skillsAgent: "claude-code",
|
||||
dir: join(homedir(), ".claude", "skills"),
|
||||
isGlobal: true,
|
||||
defaultEnabled: true,
|
||||
},
|
||||
{
|
||||
@@ -45,6 +49,7 @@ const TARGETS: Target[] = [
|
||||
flag: "gemini",
|
||||
skillsAgent: "gemini-cli",
|
||||
dir: join(homedir(), ".gemini", "skills"),
|
||||
isGlobal: true,
|
||||
defaultEnabled: true,
|
||||
},
|
||||
{
|
||||
@@ -52,8 +57,10 @@ const TARGETS: Target[] = [
|
||||
flag: "codex",
|
||||
skillsAgent: "codex",
|
||||
dir: join(homedir(), ".codex", "skills"),
|
||||
isGlobal: true,
|
||||
defaultEnabled: true,
|
||||
},
|
||||
// -- Project-level targets (opt-in via flag) --
|
||||
{
|
||||
name: "Cursor",
|
||||
flag: "cursor",
|
||||
@@ -61,6 +68,47 @@ const TARGETS: Target[] = [
|
||||
get dir() {
|
||||
return join(process.cwd(), ".cursor", "skills");
|
||||
},
|
||||
isGlobal: false,
|
||||
defaultEnabled: false,
|
||||
},
|
||||
{
|
||||
name: "Windsurf",
|
||||
flag: "windsurf",
|
||||
skillsAgent: "windsurf",
|
||||
get dir() {
|
||||
return join(process.cwd(), ".windsurf", "skills");
|
||||
},
|
||||
isGlobal: false,
|
||||
defaultEnabled: false,
|
||||
},
|
||||
{
|
||||
name: "Cline",
|
||||
flag: "cline",
|
||||
skillsAgent: "cline",
|
||||
get dir() {
|
||||
return join(process.cwd(), ".cline", "skills");
|
||||
},
|
||||
isGlobal: false,
|
||||
defaultEnabled: false,
|
||||
},
|
||||
{
|
||||
name: "Roo Code",
|
||||
flag: "roo",
|
||||
skillsAgent: "roo",
|
||||
get dir() {
|
||||
return join(process.cwd(), ".roo", "skills");
|
||||
},
|
||||
isGlobal: false,
|
||||
defaultEnabled: false,
|
||||
},
|
||||
{
|
||||
name: "Trae",
|
||||
flag: "trae",
|
||||
skillsAgent: "trae",
|
||||
get dir() {
|
||||
return join(process.cwd(), ".trae", "skills");
|
||||
},
|
||||
isGlobal: false,
|
||||
defaultEnabled: false,
|
||||
},
|
||||
];
|
||||
@@ -234,6 +282,29 @@ async function fallbackInstall(targets: Target[]): Promise<{
|
||||
return { count: allInstalled.length, installed: allInstalled, skipped };
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Shared helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function splitAgents(targets: Target[]): { global: string[]; project: string[] } {
|
||||
return {
|
||||
global: targets.filter((t) => t.isGlobal).map((t) => t.skillsAgent),
|
||||
project: targets.filter((t) => !t.isGlobal).map((t) => t.skillsAgent),
|
||||
};
|
||||
}
|
||||
|
||||
async function installSource(
|
||||
repo: string,
|
||||
agents: { global: string[]; project: string[] },
|
||||
): Promise<void> {
|
||||
if (agents.global.length > 0) {
|
||||
await runSkillsAdd(repo, agents.global, true);
|
||||
}
|
||||
if (agents.project.length > 0) {
|
||||
await runSkillsAdd(repo, agents.project, false);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Programmatic API — used by init command
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -247,8 +318,8 @@ export async function installAllSkills(
|
||||
const targets = targetNames
|
||||
? TARGETS.filter((t) => targetNames.includes(t.flag))
|
||||
: TARGETS.filter((t) => t.defaultEnabled);
|
||||
const agents = targets.map((t) => t.skillsAgent);
|
||||
const progress = options?.onProgress;
|
||||
const agents = splitAgents(targets);
|
||||
|
||||
// Try npx skills add first
|
||||
if (hasNpx()) {
|
||||
@@ -257,7 +328,7 @@ export async function installAllSkills(
|
||||
for (const source of SOURCES) {
|
||||
try {
|
||||
progress?.(`Installing ${source.name} skills...`);
|
||||
await runSkillsAdd(source.repo, agents, true);
|
||||
await installSource(source.repo, agents);
|
||||
count += 1;
|
||||
} catch {
|
||||
skipped.push(source.name);
|
||||
@@ -294,7 +365,7 @@ async function runInstall({ args }: { args: Record<string, unknown> }): Promise<
|
||||
clack.intro(c.bold("hyperframes skills"));
|
||||
|
||||
const targets = resolveTargets(args);
|
||||
const agents = targets.map((t) => t.skillsAgent);
|
||||
const agents = splitAgents(targets);
|
||||
|
||||
// Try npx skills add
|
||||
if (hasNpx()) {
|
||||
@@ -305,7 +376,7 @@ async function runInstall({ args }: { args: Record<string, unknown> }): Promise<
|
||||
const spinner = clack.spinner();
|
||||
spinner.start(`Installing ${source.name} skills...`);
|
||||
try {
|
||||
await runSkillsAdd(source.repo, agents, true);
|
||||
await installSource(source.repo, agents);
|
||||
installed.push(source.name);
|
||||
spinner.stop(c.success(`${source.name} skills installed`));
|
||||
} catch {
|
||||
@@ -364,7 +435,25 @@ async function runInstall({ args }: { args: Record<string, unknown> }): Promise<
|
||||
export default defineCommand({
|
||||
meta: {
|
||||
name: "skills",
|
||||
description: "Install HyperFrames and GSAP skills for AI coding tools",
|
||||
description: `Install HyperFrames and GSAP skills for AI coding tools
|
||||
|
||||
Global targets (enabled by default):
|
||||
--claude Claude Code (~/.claude/skills/)
|
||||
--gemini Gemini CLI (~/.gemini/skills/)
|
||||
--codex Codex CLI (~/.codex/skills/)
|
||||
|
||||
Project-level targets (opt-in):
|
||||
--cursor Cursor (.cursor/skills/)
|
||||
--windsurf Windsurf (.windsurf/skills/)
|
||||
--cline Cline (.cline/skills/)
|
||||
--roo Roo Code (.roo/skills/)
|
||||
--trae Trae (.trae/skills/)
|
||||
|
||||
Examples:
|
||||
hyperframes skills # install to Claude, Gemini, Codex
|
||||
hyperframes skills --windsurf # install to Windsurf (project-level)
|
||||
hyperframes skills --cursor --cline # install to Cursor + Cline
|
||||
hyperframes skills --claude --windsurf # mix global + project targets`,
|
||||
},
|
||||
args: {
|
||||
claude: { type: "boolean", description: "Install to Claude Code (~/.claude/skills/)" },
|
||||
@@ -374,6 +463,22 @@ export default defineCommand({
|
||||
type: "boolean",
|
||||
description: "Install to Cursor (.cursor/skills/ in current project)",
|
||||
},
|
||||
windsurf: {
|
||||
type: "boolean",
|
||||
description: "Install to Windsurf (.windsurf/skills/ in current project)",
|
||||
},
|
||||
cline: {
|
||||
type: "boolean",
|
||||
description: "Install to Cline (.cline/skills/ in current project)",
|
||||
},
|
||||
roo: {
|
||||
type: "boolean",
|
||||
description: "Install to Roo Code (.roo/skills/ in current project)",
|
||||
},
|
||||
trae: {
|
||||
type: "boolean",
|
||||
description: "Install to Trae (.trae/skills/ in current project)",
|
||||
},
|
||||
"human-friendly": { type: "boolean", description: "Enable interactive terminal UI" },
|
||||
},
|
||||
run: runInstall,
|
||||
|
||||
@@ -82,7 +82,7 @@ export const mediaRules: Array<(ctx: LintContext) => HyperframeLintFinding[]> =
|
||||
const timedTagPositions: Array<{ name: string; start: number; id?: string }> = [];
|
||||
for (const tag of tags) {
|
||||
if (tag.name === "video" || tag.name === "audio") continue;
|
||||
if (readAttr(tag.raw, "data-start")) {
|
||||
if (readAttr(tag.raw, "data-start") && !readAttr(tag.raw, "data-composition-id")) {
|
||||
timedTagPositions.push({
|
||||
name: tag.name,
|
||||
start: tag.index,
|
||||
|
||||
Reference in New Issue
Block a user