Skip to content
Open
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ Try a prompt like:

> Using `/hyperframes`, create a 10-second product intro with a fade-in title, a background video, and subtle background music.
The skills teach agents the HyperFrames production loop: plan the video, write valid HTML, wire seekable animations, add media, lint, preview, and render. They work with Claude Code, Cursor, Gemini CLI, Codex, and other coding agents that support skills.
The skills teach agents the HyperFrames production loop: plan the video, write valid HTML, wire seekable animations, add media, lint, preview, and render. They work with IBM Bob, Claude Code, Cursor, Gemini CLI, Codex, and other coding agents that support skills.

## Skills

Expand Down
12 changes: 12 additions & 0 deletions docs/guides/skills.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,18 @@ Install or refresh one workflow explicitly:
npx hyperframes skills update pr-to-video
```

## Use the skills in IBM Bob

Run `npx hyperframes skills update`, then open or restart Bob Shell in the
project folder so it discovers the skills linked under `~/.bob/skills`:

```bash
bob
```

Ask Bob to use `/hyperframes` for the first request. Running the update again
refreshes the same global skill bundles without changing the project format.

## Use the skills in Google Antigravity

Install the skills from the root of the project you open in Antigravity:
Expand Down
32 changes: 32 additions & 0 deletions packages/cli/src/telemetry/agent_runtime.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import { HINT_KEY_PATTERN } from "./agent_runtime.js";
// detectSandboxRuntime is exercised through a small set of node:os mocks.

const VENDOR_ENV_KEYS = [
"BOBSHELL_CLI",
"BOBSHELL_API_KEY",
"BOBSHELL_NO_RELAUNCH",
"CLAUDECODE",
"CLAUDE_CODE_ENTRYPOINT",
"CODEX_THREAD_ID",
Expand Down Expand Up @@ -66,6 +69,35 @@ describe("detectAgentRuntime — base behavior", () => {
});
});

describe("detectAgentRuntime — IBM Bob Shell", () => {
const savedEnv = { ...process.env };
beforeEach(stripVendorEnv);
afterEach(() => {
process.env = { ...savedEnv };
});

it("detects via BOBSHELL_CLI (set on both shell execution paths)", async () => {
process.env["BOBSHELL_CLI"] = "1";
const { detectAgentRuntime } = await import("./agent_runtime.js");
expect(detectAgentRuntime()).toBe("bob");
});

it("wins over inherited outer-agent markers as the immediate executor", async () => {
process.env["BOBSHELL_CLI"] = "1";
process.env["CLAUDECODE"] = "1";
process.env["CODEX_THREAD_ID"] = "thread-1";
const { detectAgentRuntime } = await import("./agent_runtime.js");
expect(detectAgentRuntime()).toBe("bob");
});

it("does not treat Bob authentication or relaunch state as execution proof", async () => {
process.env["BOBSHELL_API_KEY"] = "secret-not-read";
process.env["BOBSHELL_NO_RELAUNCH"] = "true";
const { detectAgentRuntime } = await import("./agent_runtime.js");
expect(detectAgentRuntime()).toBeNull();
});
});

describe("detectAgentRuntime — Claude Code", () => {
const savedEnv = { ...process.env };
beforeEach(stripVendorEnv);
Expand Down
13 changes: 13 additions & 0 deletions packages/cli/src/telemetry/agent_runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { detectWSL } from "./platform.js";
export type SandboxRuntime = "gvisor" | "firecracker" | "docker" | "kvm" | "wsl" | null;

export type AgentRuntime =
| "bob"
| "claude_code"
| "codex"
| "cursor"
Expand All @@ -47,6 +48,18 @@ interface VendorRule {
// before more generic ones (e.g. copilot_agent before a hypothetical generic
// 'github_actions' rule).
const VENDOR_RULES: VendorRule[] = [
// IBM Bob Shell — both shell execution paths stamp BOBSHELL_CLI=1 onto the
// child environment: childProcessFallback's spawn env and executeWithPty's
// PTY env. Keep this ahead of other agents because Bob inherits the parent
// environment, so a Bob session launched from Codex or Claude can carry both
// vendors' markers; BOBSHELL_CLI identifies the immediate executor.
// BOBSHELL_API_KEY is deliberately ignored: it is an authentication setting,
// not proof that Bob launched the current process.
// Source: IBM Bob Shell 1.0.6 bundle (lb.childProcessFallback / executeWithPty).
{
name: "bob",
check: (env) => typeof env["BOBSHELL_CLI"] === "string",
},
// Anthropic Claude Code — sets CLAUDECODE=1 on every Bash/PowerShell tool
// spawn (Shell.ts:321) and CLAUDE_CODE_ENTRYPOINT at startup, inherited by
// every child (main.tsx:527). Both propagate to spawned subprocesses.
Expand Down
9 changes: 9 additions & 0 deletions packages/cli/src/utils/skillsMirror.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ describe("mirrorGlobalSkills", () => {
const home = makeHome();
seedStore(home, ["hyperframes", "hyperframes-core"]);
installMarker(home, ".cursor"); // cursor present
installMarker(home, ".bob"); // IBM Bob present
installMarker(home, ".config/goose"); // goose present (XDG base)
// windsurf NOT installed (no ~/.codeium/windsurf)

Expand All @@ -130,6 +131,7 @@ describe("mirrorGlobalSkills", () => {
});
const agents = mirrored.map((m) => m.agent);
expect(agents).toContain("cursor");
expect(agents).toContain("bob");
expect(agents).toContain("goose");
expect(agents).not.toContain("windsurf");

Expand All @@ -139,6 +141,12 @@ describe("mirrorGlobalSkills", () => {
expect(realpathSync(link)).toBe(realpathSync(join(home, ".claude", "skills", "hyperframes")));
expect(existsSync(join(link, "SKILL.md"))).toBe(true);

const bobLink = join(home, ".bob", "skills", "hyperframes");
expect(lstatSync(bobLink).isSymbolicLink()).toBe(true);
expect(realpathSync(bobLink)).toBe(
realpathSync(join(home, ".claude", "skills", "hyperframes")),
);

// goose lands in the XDG config dir (~/.config/goose), not ~/.goose
expect(
existsSync(join(home, ".config", "goose", "skills", "hyperframes-core", "SKILL.md")),
Expand Down Expand Up @@ -283,6 +291,7 @@ describe("AGENT_GLOBAL_DIRS (generated table)", () => {
const byAgent = new Map(AGENT_GLOBAL_DIRS.map((e) => [e.agent, e]));
expect(byAgent.get("claude-code")).toMatchObject({ base: "claudeHome", sub: "skills" });
expect(byAgent.get("cursor")).toMatchObject({ base: "home", sub: ".cursor/skills" });
expect(byAgent.get("bob")).toMatchObject({ base: "home", sub: ".bob/skills" });
expect(byAgent.get("codex")).toMatchObject({ base: "codexHome", sub: "skills" });
expect(byAgent.get("goose")).toMatchObject({ base: "configHome", sub: "goose/skills" });
expect(byAgent.get("windsurf")).toMatchObject({
Expand Down