feat(cli): add IBM Bob runtime support (#3930)

This commit is contained in:
Brun3y
2026-09-22 22:01:42 +00:00
committed by GitHub
parent d4485b51f1
commit 105984cac4
5 changed files with 67 additions and 1 deletions
+1 -1
View File
@@ -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
+12
View File
@@ -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:
@@ -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",
@@ -96,6 +99,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);
@@ -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"
@@ -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.
@@ -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)
@@ -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");
@@ -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")),
@@ -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({