Skip to content

Implement CliRuntimeFactory #19

@alexey-pelykh

Description

@alexey-pelykh

Context

Factory module that maps provider name strings to AgentRuntime instances. This is the single entry point for runtime instantiation — callers never construct runtime classes directly.

src/middleware/
├── types.ts                       ← AgentRuntime interface (PR #4)
├── cli-runtime-base.ts            ← CLIRuntimeBase abstract class (PR #6)
├── runtimes/
│   ├── claude.ts                  ← ClaudeCliRuntime (#8, PR #9)
│   ├── gemini.ts                  ← GeminiCliRuntime (#10, PR #11)
│   ├── codex.ts                   ← CodexCliRuntime  (#12, PR #13)
│   └── opencode.ts                ← OpenCodeCliRuntime (OpenCodeCliRuntime (#16), PR #17)
└── runtime-factory.ts             ← THIS ISSUE

Dependencies: all four CLI runtimes (PRs #9, #11, #13, #17 — all merged).


Specification

createCliRuntime(provider: string): AgentRuntime

Exported function that maps a provider name string to a new runtime instance.

Provider mapping:

Input string Runtime class CLI binary
"claude" ClaudeCliRuntime claude
"gemini" GeminiCliRuntime gemini
"codex" CodexCliRuntime codex
"opencode" OpenCodeCliRuntime opencode

Behavior:

  • Input is case-insensitive: "Claude", "CLAUDE", "claude" all map to ClaudeCliRuntime
  • Whitespace is trimmed: " claude " maps to ClaudeCliRuntime
  • Unknown provider names throw a descriptive Error:
    Unknown runtime provider "foo". Supported providers: claude, gemini, codex, opencode
    
  • Returns a new instance on each call (no singleton/caching — callers may need separate state)

Implementation approach

import { ClaudeCliRuntime } from "./runtimes/claude.js";
import { GeminiCliRuntime } from "./runtimes/gemini.js";
import { CodexCliRuntime } from "./runtimes/codex.js";
import { OpenCodeCliRuntime } from "./runtimes/opencode.js";
import type { AgentRuntime } from "./types.js";

const SUPPORTED_PROVIDERS = ["claude", "gemini", "codex", "opencode"] as const;

type SupportedProvider = (typeof SUPPORTED_PROVIDERS)[number];

export function createCliRuntime(provider: string): AgentRuntime {
    const normalized = provider.trim().toLowerCase();

    switch (normalized) {
        case "claude":
            return new ClaudeCliRuntime();
        case "gemini":
            return new GeminiCliRuntime();
        case "codex":
            return new CodexCliRuntime();
        case "opencode":
            return new OpenCodeCliRuntime();
        default:
            throw new Error(
                `Unknown runtime provider "${provider}". Supported providers: ${SUPPORTED_PROVIDERS.join(", ")}`
            );
    }
}

Exports

The module should also export:

  • SUPPORTED_PROVIDERS — the array of supported provider name strings (for validation, help text, etc.)
  • SupportedProvider — the union type of supported provider names

Test file specification

Create src/middleware/runtime-factory.test.ts.

Test categories

Provider mapping (~5 tests):

  • "claude" returns instance of ClaudeCliRuntime
  • "gemini" returns instance of GeminiCliRuntime
  • "codex" returns instance of CodexCliRuntime
  • "opencode" returns instance of OpenCodeCliRuntime
  • Each returned instance satisfies AgentRuntime interface (has execute method)

Input normalization (~3 tests):

  • Case-insensitive: "Claude", "GEMINI", "Codex" map correctly
  • Whitespace trimmed: " claude " maps correctly
  • Mixed case + whitespace: " OpenCode " maps correctly

Error handling (~2 tests):

  • Unknown provider throws Error with descriptive message including the invalid name
  • Error message lists all supported providers

Instance freshness (~1 test):

  • Two calls with same provider return distinct instances (not the same reference)

SUPPORTED_PROVIDERS export (~1 test):

  • Contains exactly ["claude", "gemini", "codex", "opencode"]

Total: ~12 tests


Acceptance criteria

  • src/middleware/runtime-factory.ts exports createCliRuntime function
  • src/middleware/runtime-factory.ts exports SUPPORTED_PROVIDERS array
  • src/middleware/runtime-factory.ts exports SupportedProvider type
  • All 4 providers map to correct runtime class instances
  • Input is normalized (case-insensitive, trimmed)
  • Unknown providers throw descriptive Error listing supported providers
  • Each call returns a new instance (no singleton caching)
  • src/middleware/runtime-factory.test.ts covers all specified categories (~12 tests)
  • All tests pass via npx vitest run src/middleware/runtime-factory.test.ts

References

  • src/middleware/types.tsAgentRuntime interface
  • src/middleware/runtimes/claude.tsClaudeCliRuntime
  • src/middleware/runtimes/gemini.tsGeminiCliRuntime
  • src/middleware/runtimes/codex.tsCodexCliRuntime
  • src/middleware/runtimes/opencode.tsOpenCodeCliRuntime

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions