@nlindstedt/zig-workflow
v0.9.0
Published
TypeScript SDK for zig — workflow orchestration engine for AI coding agents
Downloads
1,030
Maintainers
Readme
Zig TypeScript Binding
TypeScript binding for zig — a workflow orchestration engine for AI coding agents.
Prerequisites
- Node.js 18+
- The
zigCLI binary installed and on yourPATH(or set viaZIG_BINenv var) - At least one AI agent CLI (
claude,codex,gemini,copilot,ollama) for workflow execution
Installation
npm install @nlindstedt/zig-workflowDevelopment setup
To work with the binding from source:
cd bindings/typescript
npm install
npm run buildQuick start
import { ZigBuilder } from "@nlindstedt/zig-workflow";
// Run a workflow
const output = await new ZigBuilder()
.run("deploy-pipeline");
console.log(output);Running workflows
import { ZigBuilder } from "@nlindstedt/zig-workflow";
// Non-interactive — capture stdout
const output = await new ZigBuilder()
.run("my-workflow", "additional context");
// Interactive — inherits terminal stdio
await new ZigBuilder()
.runInteractive("my-workflow");
// Stream output lines as they arrive
for await (const line of new ZigBuilder().stream("my-workflow")) {
console.log(line);
}Bidirectional streaming sessions
runStreaming() returns a StreamingSession with piped stdin for sending
input mid-flight. Call .close({ timeout }) when you are done to shut the
session down gracefully:
const session = new ZigBuilder()
.autoCleanup()
.runStreaming("interactive-workflow");
for await (const line of session.lines()) {
console.log(line);
}
await session.close({ timeout: "5s" });Automatic orphan cleanup
Long-running Node servers can leak agent subprocesses if the parent process
dies unexpectedly. Opt in to .autoCleanup() on the builder to install
process-wide shutdown handlers that SIGTERM every tracked live session:
const session = new ZigBuilder()
.autoCleanup()
.runStreaming("my-workflow");Off by default so the SDK imposes no global side effects on consumers that don't need them.
Validating workflows
import { ZigBuilder, ZigError } from "@nlindstedt/zig-workflow";
try {
const msg = await new ZigBuilder().validate("deploy.zwf");
console.log(msg); // "workflow 'deploy' is valid (3 steps)"
} catch (err) {
if (err instanceof ZigError) {
console.error("Validation failed:", err.stderr);
}
}Managing workflows
import { ZigBuilder } from "@nlindstedt/zig-workflow";
const zig = new ZigBuilder();
// List available workflows
const listing = await zig.workflowList();
console.log(listing);
// Show workflow details
const details = await zig.workflowShow("deploy");
console.log(details);
// Delete a workflow
await zig.workflowDelete("old-workflow");
// Create a workflow interactively
await zig.workflowCreate({
name: "new-workflow",
pattern: "fan-out",
});Parsing .zwf files
The SDK includes a lightweight TOML parser for reading .zwf workflow files
directly from Node.js without spawning the CLI:
import { parseWorkflow, parseWorkflowFile } from "@nlindstedt/zig-workflow";
// Parse from a string
const workflow = parseWorkflow(`
[workflow]
name = "example"
description = "An example workflow"
[[step]]
name = "greet"
prompt = "Say hello"
provider = "claude"
model = "sonnet"
`);
console.log(workflow.workflow.name); // "example"
console.log(workflow.steps[0].provider); // "claude"
// Parse from a file
const wf = await parseWorkflowFile("./deploy.zwf");
console.log(wf.steps.length);Builder methods
| Method | Description |
|--------|-------------|
| .bin(path) | Override the zig binary path (default: ZIG_BIN env or "zig") |
| .debug() | Enable debug logging |
| .quiet() | Suppress all output except errors |
| .autoCleanup(enabled?) | Install process-wide shutdown handlers for orphan cleanup |
Terminal methods
| Method | Returns | Description |
|--------|---------|-------------|
| .run(workflow, prompt?) | Promise<string> | Run a workflow non-interactively, return stdout |
| .runInteractive(workflow, prompt?) | Promise<void> | Run a workflow interactively (inherits stdio) |
| .stream(workflow, prompt?) | AsyncGenerator<string> | Stream stdout lines as they arrive |
| .runStreaming(workflow, prompt?) | StreamingSession | Bidirectional streaming with piped stdin/stdout |
| .validate(workflow) | Promise<string> | Validate a .zwf file |
| .workflowList() | Promise<string> | List available workflows |
| .workflowShow(workflow) | Promise<string> | Show workflow details |
| .workflowDelete(workflow) | Promise<string> | Delete a workflow |
| .workflowCreate(options?) | Promise<void> | Create a workflow interactively |
| .listen(options?) | Promise<void> | Tail a running/completed session |
| .workflowPack(path, output?) | Promise<string> | Pack a workflow directory into a .zwfz zip archive |
| .man(topic?) | Promise<string> | Show a manual page topic |
Utility functions
| Function | Returns | Description |
|----------|---------|-------------|
| parseWorkflow(content) | Workflow | Parse a TOML .zwf string into a typed Workflow object |
| parseWorkflowFile(path) | Promise<Workflow> | Read and parse a .zwf file from disk |
| zagSessionName(workflow, step) | string | Compute the zag session name for a single step (zig-{workflow}-{step}) |
| zagSessionNames(workflow) | Record<string, string> | Extract all zag session names from a parsed workflow |
Workflow types
The SDK exports TypeScript types that mirror the Rust data model:
import type {
Workflow,
WorkflowMeta,
Role,
Variable,
VarType,
Step,
FailurePolicy,
StepCommand,
Pattern,
} from "@nlindstedt/zig-workflow";Orchestration patterns
The Pattern type covers the seven orchestration patterns supported by zig:
| Pattern | Description |
|---------|-------------|
| "sequential" | Steps run in order, each feeding the next |
| "fan-out" | Parallel independent steps, then synthesize |
| "generator-critic" | Generate, evaluate, iterate until quality threshold |
| "coordinator-dispatcher" | Classify input, route to specialized handlers |
| "hierarchical-decomposition" | Break down into sub-tasks, delegate, synthesize |
| "human-in-the-loop" | Automated steps with human approval gates |
| "inter-agent-communication" | Agents collaborate via shared variables |
Bridging to zag-agent
Zig names each zag session deterministically as zig-{workflowName}-{stepName}.
The SDK exposes helpers to compute these names so you can use
@nlindstedt/zag-agent
to control individual agent sessions spawned by a workflow:
import { parseWorkflowFile, zagSessionName, zagSessionNames } from "@nlindstedt/zig-workflow";
import { ZagBuilder } from "@nlindstedt/zag-agent";
// Single step session name
const session = zagSessionName("deploy", "lint");
// "zig-deploy-lint"
// All session names from a workflow file
const wf = await parseWorkflowFile("deploy.zwf");
const sessions = zagSessionNames(wf);
// { lint: "zig-deploy-lint", test: "zig-deploy-test", deploy: "zig-deploy-deploy" }
// Use with zag-agent to control the agent session
const output = await new ZagBuilder()
.session(sessions.lint)
.continueLast();Error handling
import { ZigBuilder, ZigError, ZigVersionError } from "@nlindstedt/zig-workflow";
try {
await new ZigBuilder().run("my-workflow");
} catch (err) {
if (err instanceof ZigVersionError) {
console.error(`Requires zig >= ${err.requiredVersion}, have ${err.installedVersion}`);
} else if (err instanceof ZigError) {
console.error("Process failed:");
console.error(" Exit code:", err.exitCode);
console.error(" Stderr:", err.stderr);
} else {
throw err;
}
}How it works
The SDK spawns the zig CLI as a subprocess and captures stdout/stderr.
Zero external runtime dependencies — only Node.js built-ins.
Testing
npm run build && npm testSee also
- Zag TypeScript SDK (
@nlindstedt/zag-agent) — Lower-level agent control - Zig CLI — The zig command-line tool
- .zwf Format Reference — Full .zwf/.zwfz file specification
