TypeScript SDK for the ForgeCode CLI (forge binary). Wraps the forge CLI with a programmatic async-generator API that follows the Claude Agent SDK pattern.
bun add github:ImBIOS/forgecode-sdkNote
Not publishing to npm is good enough for our company use-case, you can request me to publish it, just vote in GitHub issues of this repo.
import { query } from "@imbios/forgecode-sdk";
for await (const message of query({
prompt: "Fix the bug in auth.ts",
options: { agent: "forge" },
})) {
switch (message.type) {
case "system":
console.log(`Session: ${message.session_id}`);
break;
case "assistant":
process.stdout.write(message.content);
break;
case "result":
console.log("\nDone:", message.result);
break;
case "error":
console.error("Error:", message.error);
break;
}
}Requires Bun >= 1.0.0 and the forge CLI binary installed.
# Install the SDK
bun add github:ImBIOS/forgecode-sdk
# Install forge (if not already installed)
curl -fsSL https://forgecode.dev/cli | shThe SDK resolves the forge binary in this order:
FORGE_PATHenvironment variableconfig.forgePath(global config)~/.local/bin/forgeforgeon system PATH
The main entry point. Spawns the forge binary and yields typed messages as they arrive.
import type { ForgeMessage, QueryOptions, ForgeConfig } from "@imbios/forgecode-sdk";
for await (const message of query({
prompt: string,
options?: QueryOptions,
}, config?: ForgeConfig)): ForgeMessage { ... }| Type | Fields | Description |
|---|---|---|
system |
subtype: "init", session_id: string |
Emitted at session start |
assistant |
content: string |
Streaming text chunk from the agent |
result |
result: string, session_id: string, usage?: { input_tokens?, output_tokens? } |
Final result when the agent finishes |
tool_use |
name: string, arguments: Record<string, unknown> |
Tool call performed during execution |
error |
error: string, exitCode?: number |
Error encountered during execution |
| Option | Type | Description |
|---|---|---|
agent |
string |
Agent ID. Maps to forge --agent <id> |
model |
string |
Model to use. Maps to FORGE_MODEL env var |
systemPrompt |
string |
Prepended to the user prompt |
cwd |
string |
Working directory. Maps to forge --directory <path> |
env |
Record<string, string | undefined> |
Environment variables for the forge process |
outputFormat |
{ type: "json_schema", z: ZodType, verboseErrors?: boolean } |
Structured JSON output with Zod validation |
maxTurns |
number |
Max conversation turns. Maps to forge --max-turns <n> |
allowedTools |
string[] |
Tools the agent is allowed to use |
disallowedTools |
string[] |
Disallowed tool names. Maps to forge --disallowed-tools |
tools |
string[] |
Available built-in tools. Maps to forge --tools. [] disables all |
continue |
boolean |
Continue most recent conversation. Maps to forge --continue |
resume |
string |
Session ID to resume. Maps to forge --resume <id> |
conversationId |
string |
Conversation ID. Maps to forge --conversation-id <id> |
sandbox |
string |
Git worktree sandbox name. Maps to forge --sandbox <name> |
reasoningEffort |
"none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max" |
Reasoning effort level |
mcpServers |
Record<string, McpServerConfig> |
MCP servers to import before the run |
abortController |
AbortController |
Cancel the query (kills forge process) |
stderr |
(data: string) => void |
Real-time stderr callback |
title |
string |
Custom session title. Maps to forge --title <title> |
const config: ForgeConfig = {
forgePath?: string, // Explicit binary path
openaiUrl?: string, // API base URL (OPENAI_URL env var)
openaiApiKey?: string, // API key (OPENAI_API_KEY env var)
model?: string, // Default model (FORGE_MODEL env var)
reasoningEffort?: ReasoningEffort,
};
// Pass as second argument to query()
for await (const msg of query({ prompt: "..." }, config)) { ... }Returns the resolved path to the forge binary. Throws ForgeBinaryNotFoundError if not found.
import { resolveForgePath } from "@imbios/forgecode-sdk";
const path = resolveForgePath(); // "/home/user/.local/bin/forge"Attempts to extract a JSON object from text that may contain markdown fences or surrounding text. Tries four strategies in order:
```json ... ```fenced block``` ... ```generic fenced block- First
{... last}substring - Entire text as JSON
import { extractJsonFromText } from "@imbios/forgecode-sdk";
const obj = extractJsonFromText('Here is the result:\n```json\n{"x": 1}\n```');
// obj === { x: 1 }| Class | When |
|---|---|
ForgeBinaryNotFoundError |
forge binary not found on PATH |
ForgeProcessError |
Forge exited with non-zero code |
ForgeOutputParseError |
JSON extraction from output failed |
ForgeAbortError |
Query cancelled via AbortController |
| Example | Description |
|---|---|
| basic-query.ts | Send a prompt and collect the result |
| json-output.ts | Request structured JSON output with schema validation |
| abort-query.ts | Cancel a long-running query with AbortController |
| tool-use.ts | Capture tool use events during execution |
| advanced-options.ts | Model selection, max turns, env vars, system prompt, stderr |
| session-management.ts | Continue and resume conversations |
| error-handling.ts | Handle SDK errors gracefully |
| mcp-servers.ts | Import MCP servers before a query |
Run any example:
bun run examples/basic-query.tsThe forge CLI has no --output-format flag. The SDK handles structured output by:
- Injecting a system prompt that instructs the agent to produce JSON matching the
zschema - Auto-deriving a JSON Schema from
zviaz.toJSONSchema(z)for the agent's benefit - After the process completes, calling
extractJsonFromText()on the final output - If
zis aZodType, validating the extracted JSON withz.parse()— throws a clear error with Zod issues on failure - Yielding
message.resultas a fully typed object (no extraJSON.parseneeded)
// zodSchema → typed result object (no JSON.parse needed)
outputFormat: { type: "json_schema", z: z.object({ name: z.string() }) }
// no schema → result is a string
outputFormat: { type: "json_schema" }The SDK follows the same mental model as @anthropic-ai/claude-agent-sdk:
// Claude Agent SDK
import { query } from "@anthropic-ai/claude-agent-sdk";
for await (const msg of query({ prompt, options })) { ... }
// ForgeCode SDK (same pattern)
import { query } from "@imbios/forgecode-sdk";
for await (const msg of query({ prompt, options })) { ... }Key differences from Claude Agent SDK:
- ForgeCode SDK uses
Bun.spawn(requires Bun runtime) - No permission system (forge runs autonomously)
- No hook system (PreToolUse, PostToolUse, etc.)
- No session management API (listSessions, getSessionMessages, etc.)
outputFormatis post-processed, not enforced by the APIsystemPromptis a string (not array/preset object)
bun install
bun run typecheck