The orchestrator invokes AI coding agents through the AgentRunner interface. Runners are auto-discovered from environment variables via the RunnerRegistry.
Every runner implements:
interface AgentRunner {
run(ctx: AgentContext): Promise<AgentResult>;
}The orchestrator provides context (issue details, codebase profile, constraints) and the runner spawns the agent, collects output, and commits changes.
Invokes Claude Code CLI in --print mode.
| Property | Value |
|---|---|
| CLI command | claude -p --model <model> --allowedTools <tools> |
| stdin | Prompt sent via stdin |
| Auth | ANTHROPIC_API_KEY (inherited from environment) |
| Model override | AI_SDLC_MODEL env var (default: claude-sonnet-4-5-20250929) |
| Registration | Always available (built-in) |
import { ClaudeCodeRunner } from '@ai-sdlc/orchestrator';Invokes GitHub Copilot CLI in --yolo autonomous mode.
| Property | Value |
|---|---|
| CLI command | copilot -p <prompt> --yolo [--model <model>] |
| stdin | None (prompt is a CLI argument) |
| Auth | GH_TOKEN or GITHUB_TOKEN (passed through environment) |
| Model override | AI_SDLC_COPILOT_MODEL env var |
| Registration | Available when GH_TOKEN or GITHUB_TOKEN is set |
import { CopilotRunner } from '@ai-sdlc/orchestrator';Invokes Cursor CLI agent with NDJSON stream output.
| Property | Value |
|---|---|
| CLI command | cursor-agent --print <prompt> --force --output-format=stream-json [-m <model>] |
| stdin | None (prompt is a CLI argument) |
| Auth | CURSOR_API_KEY |
| Model override | AI_SDLC_CURSOR_MODEL env var |
| Registration | Available when CURSOR_API_KEY is set |
The runner parses NDJSON output to extract the final assistant message for use as the PR summary.
import { CursorRunner, parseStreamJson } from '@ai-sdlc/orchestrator';Invokes OpenAI Codex CLI in --full-auto --json mode.
| Property | Value |
|---|---|
| CLI command | codex exec - --full-auto --json [-m <model>] |
| stdin | Prompt written to stdin (Codex reads from -) |
| Auth | CODEX_API_KEY |
| Model override | AI_SDLC_CODEX_MODEL env var |
| Registration | Available when CODEX_API_KEY is set |
The runner includes an enhanced token usage parser that first tries NDJSON usage/token_usage events from stderr (accumulating across multiple events), then falls back to regex.
import { CodexRunner, parseTokenUsage } from '@ai-sdlc/orchestrator';Invokes any OpenAI-compatible chat completions API over HTTP.
| Property | Value |
|---|---|
| Transport | HTTP POST to /v1/chat/completions endpoint |
| Auth | API key via Authorization: Bearer header |
| Registration | Available when OPENAI_API_KEY or LLM_API_KEY + LLM_API_URL is set |
import { GenericLLMRunner } from '@ai-sdlc/orchestrator';The ai-sdlc run command accepts a --runner <name> flag to select any registered runner by name:
ai-sdlc run --issue 42 --runner copilot
ai-sdlc run --issue 42 --runner cursor
ai-sdlc run --issue 42 --runner claude-code # explicit built-in defaultIf the specified name is not registered, the command fails immediately with an actionable error listing the available runners — no silent fallback.
To plug in a custom runner (e.g. Kiro, a proprietary agent CLI, or a test double) without forking the package, set AI_SDLC_RUNNER_PLUGIN to the path of a module that exports an AgentRunner:
export AI_SDLC_RUNNER_PLUGIN=/path/to/my-runner.mjs
ai-sdlc run --issue 42The module must export a default export or a named runner export satisfying the AgentRunner interface:
// my-runner.mjs (ESM)
export default {
async run(ctx) {
// Invoke your agent, commit changes, return AgentResult
return { success: true, filesChanged: ['src/fix.ts'], summary: 'Done.' };
},
};Or with a named export:
// my-runner.mjs
export const runner = {
async run(ctx) { /* ... */ }
};If the module cannot be imported or does not export a valid AgentRunner, the pipeline fails immediately with an actionable error — no silent fallback.
The full precedence chain (first match wins):
| Priority | Source | How to set |
|---|---|---|
| 1 (highest) | Programmatic injection | new Orchestrator({ runner: myRunner }) |
| 2 | --runner <name> flag |
ai-sdlc run --runner copilot |
| 3 | AI_SDLC_RUNNER_PLUGIN |
export AI_SDLC_RUNNER_PLUGIN=/path/to/runner.mjs |
| 4 (default) | ClaudeCodeRunner |
(always available) |
Env-discovered runners do not auto-select. Setting
GH_TOKEN,OPENAI_API_KEY,ANTHROPIC_API_KEY, etc. registers the corresponding runner so it is selectable by name (--runner copilot), but the mere presence of an ambient env var does not silently switch the default runner — these vars are commonly set for unrelated tools. Absent an explicit--runner/AI_SDLC_RUNNER_PLUGIN/programmatic selection, the default is alwaysClaudeCodeRunner. Select an env-discovered runner explicitly with--runner <name>.
The RunnerRegistry manages discovery and selection of runners:
import { createRunnerRegistry } from '@ai-sdlc/orchestrator';
const registry = createRunnerRegistry();
// List all available runners
const available = registry.listAvailable();
console.log(available.map(r => r.name));
// ['claude-code', 'copilot', 'cursor', ...]
// Get a specific runner
const runner = registry.get('copilot');
// Get the default runner (first available)
const defaultRunner = registry.getDefault();discoverFromEnv() registers runners based on environment variables:
| Runner | Required Env Var(s) | Source |
|---|---|---|
claude-code |
(always available) | built-in |
copilot |
GH_TOKEN or GITHUB_TOKEN |
env |
cursor |
CURSOR_API_KEY |
env |
codex |
CODEX_API_KEY |
env |
openai |
OPENAI_API_KEY |
env |
anthropic |
ANTHROPIC_API_KEY |
env |
generic-llm |
LLM_API_URL + LLM_API_KEY |
env |
You can register custom runners:
import { RunnerRegistry } from '@ai-sdlc/orchestrator';
class MyCustomRunner implements AgentRunner {
async run(ctx: AgentContext): Promise<AgentResult> {
// Custom agent invocation logic
}
}
const registry = new RunnerRegistry();
registry.register('my-agent', new MyCustomRunner());When the sandboxId field is set on AgentContext, CLI-based runners execute the agent inside an NVIDIA OpenShell sandbox. The runner prefixes the spawn command with openshell sandbox connect <id> --, so instead of:
claude -p --model claude-sonnet-4-5-20250929 --allowedTools Edit,Write,...
it becomes:
openshell sandbox connect aisdlc-issue-42-1711316400 -- claude -p --model claude-sonnet-4-5-20250929 --allowedTools Edit,Write,...
This provides kernel-level isolation (Landlock filesystem policies, seccomp syscall filtering, network policy enforcement) without any changes to the agent itself. The orchestrator's executePipeline() automatically passes sandboxId when a SecurityContext with an OpenShell sandbox is configured.
See Security > OpenShell for setup details.
All CLI-based runners (Claude Code, Copilot, Cursor, Codex) follow the same subprocess pattern:
- Build prompt —
buildPrompt(ctx)constructs a prompt from issue details, constraints, codebase context, and episodic memory - Spawn CLI — Run the agent CLI as a child process with appropriate flags
- Collect output — Buffer stdout and stderr
- Parse token usage — Extract input/output token counts from stderr for cost tracking
- Git diff — Run
git diff --name-onlyandgit ls-files --othersto find changed files - Commit —
git add -Aandgit commitwith the configured message template and co-author
| Variable | Default | Description |
|---|---|---|
AI_SDLC_RUNNER_PLUGIN |
(none) | Path to a custom runner plugin module (ESM/CJS, must export default or named runner). Fails fast on invalid module. |
AI_SDLC_MODEL |
claude-sonnet-4-5-20250929 |
Model for ClaudeCodeRunner |
AI_SDLC_COPILOT_MODEL |
(CLI default) | Model override for CopilotRunner |
AI_SDLC_CURSOR_MODEL |
(CLI default) | Model override for CursorRunner |
AI_SDLC_CODEX_MODEL |
(CLI default) | Model override for CodexRunner |
AI_SDLC_RUNNER_TIMEOUT |
900000 (15 min) |
Runner timeout in ms (supports duration strings) |
AI_SDLC_LINT_COMMAND |
(none) | Lint command injected into agent prompts |
AI_SDLC_FORMAT_COMMAND |
(none) | Format command injected into agent prompts |
AI_SDLC_COMMIT_MESSAGE_TEMPLATE |
fix: resolve issue #{issueNumber}\n\n{issueTitle} |
Commit message template |
AI_SDLC_COMMIT_CO_AUTHOR |
Claude <noreply@anthropic.com> |
Co-author for commits |