A .NET CLI engine that orchestrates multi-step AI pipelines using Claude Code as the execution backend. Define pipelines in YAML, compose agents with Markdown context bundles, and let Claude handle planning, execution, and validation.
- .NET 10 SDK (preview)
- Claude Code CLI installed and authenticated
- An Anthropic API key configured for Claude Code
npm install -g @anthropic-ai/claude-codeAfter installing, authenticate:
claude loginVerify that it works:
claude --versionFor detailed setup instructions, see the Claude Code documentation.
git clone https://github.com/your-org/code-genesis-github.git
cd code-genesis-githubdotnet builddotnet run --project CodeGenesis.Engine -- run-pipeline examples/hello-world.ymlOverride input variables from the command line:
dotnet run --project CodeGenesis.Engine -- run-pipeline examples/hello-world.yml \
--input task="Create a Python calculator" \
--input language="python"dotnet run --project CodeGenesis.Engine -- run "Add retry logic to the HttpClient service"Runs a built-in Plan > Execute > Validate pipeline for a given task description.
| Option | Description |
|---|---|
-d, --directory |
Working directory (default: current) |
-m, --model |
Claude model (e.g. claude-sonnet-4-6) |
--max-turns |
Max agentic turns per step (default: 5, 0 = unlimited) |
--skip-validate |
Skip the validation step |
Runs a pipeline defined in a YAML configuration file.
| Option | Description |
|---|---|
-i, --input |
Input override as key=value (repeatable) |
-d, --directory |
Working directory (default: current) |
-m, --model |
Model override for all steps |
Pipelines are defined in YAML files with four sections:
# ── Metadata ──────────────────────────────────────────
pipeline:
name: "My Pipeline"
description: "What this pipeline does"
version: "1.0"
# ── Global settings ──────────────────────────────────
settings:
model: "claude-sonnet-4-6" # default model for all steps
max_turns: 5 # default max turns
timeout_seconds: 300 # process timeout
working_directory: "." # working dir for Claude
# ── Input variables ──────────────────────────────────
inputs:
task:
description: "What to build"
default: "Create a hello world page"
language:
description: "Target language"
default: "html"
# ── Steps ─────────────────────────────────────────────
steps:
- name: "Plan"
agent: "architect"
description: "Create implementation plan"
model: "claude-opus-4-6" # per-step model override
context: "contexts/planner" # load a context bundle
output_key: "plan" # store output for later steps
- name: "Execute"
agent: "engineer"
description: "Implement the plan"
system_prompt: "You are a senior engineer."
prompt: |
Implement this plan:
{{steps.plan}}
Original task: {{task}}
max_turns: 10
output_key: "result"
allowed_tools:
- "Read"
- "Write"
- "Edit"
- "Bash"
- name: "Validate"
agent: "reviewer"
description: "Review the implementation"
prompt: |
Review: {{steps.result}}
Verdict: PASS or FAIL
optional: true # pipeline continues if this fails
# ── Outputs ───────────────────────────────────────────
outputs:
summary:
source: "steps.result"
description: "Final result"Use {{variable}} to reference inputs and {{steps.<output_key>}} to reference outputs from previous steps. Variables are resolved just before each step runs, so later steps always see the latest outputs.
Loop over a collection and run sub-steps for each item:
steps:
- name: "List modules"
prompt: "Return a JSON array of module names"
output_key: "modules"
- foreach:
collection: "{{steps.modules}}" # JSON array or comma-separated string
item_var: "module" # variable name for current item (default: "item")
output_key: "module_results" # aggregated results stored as JSON array
steps:
- name: "Analyze {{module}}"
prompt: "Analyze module: {{module}}"
output_key: "analysis"Collection formats — The collection field accepts a JSON array (["a","b","c"]), a comma-separated string (a,b,c), or a newline-separated string.
Loop variables — Inside foreach sub-steps you can use:
{{<item_var>}}(e.g.{{module}}) — the current item value{{loop.item}}— alias for the current item{{loop.index}}— zero-based index of the current iteration
Scoping — Each iteration gets its own context. Sub-step outputs from one iteration don't leak into the next. When output_key is set, all iteration results are aggregated into a JSON array.
Run multiple independent branches concurrently:
steps:
- parallel:
max_concurrency: 5 # optional, default = unlimited
fail_fast: true # optional, cancel siblings on first failure
branches:
- name: "Security Review"
output_key: "security"
steps:
- name: "Check vulnerabilities"
prompt: "Review for security issues"
output_key: "security"
- name: "Performance Review"
output_key: "performance"
steps:
- name: "Check performance"
prompt: "Review for performance"
output_key: "performance"| Option | Default | Description |
|---|---|---|
max_concurrency |
unlimited | Maximum branches running at the same time |
fail_fast |
false |
If true, cancel remaining branches when one fails |
Each branch runs in an isolated context. After all branches complete, their outputs are merged back into the parent context.
Iterate over a collection concurrently — combines foreach's collection parsing with parallel's concurrency model:
steps:
- name: "List modules"
prompt: "Return a JSON array of module names"
output_key: "modules"
- parallel_foreach:
collection: "{{steps.modules}}" # JSON array or comma-separated string
item_var: "module" # variable name for current item
max_concurrency: 3 # optional, default = unlimited
fail_fast: false # optional, cancel siblings on first failure
output_key: "results" # aggregated results as JSON array
steps:
- name: "Analyze {{module}}"
prompt: "Analyze module: {{module}}"
output_key: "analysis"| Option | Default | Description |
|---|---|---|
collection |
(required) | JSON array, comma-separated, or newline-separated string |
item_var |
"item" |
Variable name for the current item |
max_concurrency |
unlimited | Maximum items processing at the same time |
fail_fast |
false |
If true, cancel remaining items when one fails |
output_key |
null |
Store all iteration results as a JSON array |
Each iteration gets an isolated context with the same loop variables as foreach ({{loop.item}}, {{loop.index}}, {{<item_var>}}). Sub-step rendering is suppressed — only item-level start/completion messages are shown.
See examples/parallel-foreach.yml for a complete working example.
Foreach and parallel can be nested. For example, "for each module, run lint and test in parallel":
steps:
- foreach:
collection: "{{steps.modules}}"
item_var: "module"
steps:
- parallel:
branches:
- name: "Lint"
steps:
- name: "Lint {{module}}"
prompt: "Lint module {{module}}"
- name: "Test"
steps:
- name: "Test {{module}}"
prompt: "Test module {{module}}"See examples/foreach-parallel.yml for a complete working example.
A turn is one complete round-trip between CodeGenesis and Claude: the engine sends a prompt, Claude reasons about it, optionally calls tools (Read, Write, Edit, Bash, etc.), and returns a response. Complex tasks often require multiple turns — for example, Claude might read a file in turn 1, edit it in turn 2, and run tests in turn 3.
max_turns limits how many of these round-trips a single step is allowed to perform. This controls both cost and execution time:
| Value | Behavior |
|---|---|
0 |
Unlimited: Claude runs with no turn limit — it will keep working until the task is complete or the timeout is reached. Use this for complex, open-ended tasks where you want full autonomy. |
1 |
Single-shot: Claude responds once with no tool use. Good for planning or review steps that only need to produce text. |
3-5 |
Light agentic work: enough for reading a few files and producing a response. This is the default. |
10+ |
Deep agentic work: Claude can explore the codebase, create/edit multiple files, run commands, and iterate. Use for execution-heavy steps. |
You can set it at three levels (most specific wins):
- Per step —
max_turns: 10in a YAML step or agent frontmatter - Per pipeline —
settings.max_turns: 5in the YAML global settings - Global default —
MaxTurnsDefaultinappsettings.jsonor via--max-turnsCLI flag (default:5)
Unlimited turns from the CLI:
# Ad-hoc command with unlimited turns
dotnet run --project CodeGenesis.Engine -- run "Build a full REST API" --max-turns 0
# YAML pipeline with unlimited turns on a specific step
steps:
- name: "Execute"
max_turns: 0 # let Claude work until doneWarning: Unlimited turns can consume significant API credits and run for a long time. Make sure
timeout_secondsis configured appropriately to act as a safety net.
Context bundles let you package agent instructions as reusable Markdown directories instead of inlining everything in YAML. A step references a bundle via the context field:
steps:
- name: "Plan"
context: "contexts/planner"contexts/planner/
CONTEXT.md # General instructions (loaded first)
agents/
architect.md # Agent definition with frontmatter
skills/
plan-format/
SKILL.md # Additional skill instructions
Agent .md files support YAML frontmatter for configuration:
---
model: claude-opus-4-6
tools: Read, Grep, Glob
maxTurns: 1
prompt: "Analyze and plan the following task:\nTask: {{task}}"
---
# Software Architect
You are a senior software architect...The Markdown body becomes the system prompt. Frontmatter fields (model, tools, maxTurns, prompt) override pipeline-level settings.
When a context bundle is loaded, values are resolved in this order (first defined wins):
- Agent frontmatter (
agents/*.md) - Pipeline YAML step config
- Pipeline YAML global settings
All configuration can be set via environment variables prefixed with CODEGENESIS_:
export CODEGENESIS_Claude__CliPath="/usr/local/bin/claude"
export CODEGENESIS_Claude__DefaultModel="claude-sonnet-4-6"
export CODEGENESIS_Claude__TimeoutSeconds=600Place an appsettings.json in the Engine project for persistent configuration:
{
"Claude": {
"CliPath": "claude",
"DefaultModel": "claude-sonnet-4-6",
"TimeoutSeconds": 300,
"MaxTurnsDefault": 5
}
}| Key | Default | Description |
|---|---|---|
CliPath |
"claude" |
Path to the Claude Code CLI binary |
DefaultModel |
null |
Model used when none is specified |
TimeoutSeconds |
300 |
Max seconds before killing a process |
MaxTurnsDefault |
5 |
Default agentic turns per step (0 = unlimited) |
code-genesis-github/
Solution.slnx
examples/
hello-world.yml # Sample pipeline
foreach-parallel.yml # Foreach + parallel demo
parallel-foreach.yml # Parallel foreach demo
contexts/
planner/ # Sample context bundle
CONTEXT.md
agents/architect.md
skills/plan-format/SKILL.md
CodeGenesis.Engine/
Program.cs # Entry point, DI, Serilog, Spectre CLI
Claude/
ClaudeCliRunner.cs # Spawns `claude --print` processes
ClaudeCliOptions.cs # Configuration POCO
ClaudeRequest.cs # Request model
ClaudeResponse.cs # Response model
IClaudeRunner.cs # Abstraction for testability
Cli/
RunCommand.cs # `run` command (hardcoded pipeline)
RunPipelineCommand.cs # `run-pipeline` command (YAML-driven)
Config/
PipelineConfig.cs # YAML deserialization models
PipelineConfigLoader.cs # YAML loader + template resolver
ContextBundleLoader.cs # Loads context bundles from directories
AgentDefinition.cs # Agent config model
MarkdownFrontmatterParser.cs # Parses YAML frontmatter from .md files
Pipeline/
PipelineExecutor.cs # Runs steps sequentially (implements IStepExecutor)
PipelineContext.cs # Shared state between steps
IPipelineStep.cs # Step interface
IStepExecutor.cs # Interface for recursive sub-execution
CollectionParser.cs # Parses JSON/CSV/newline collections
StepResult.cs # Step output model
Steps/
PlanStep.cs # Built-in planning step
ExecuteStep.cs # Built-in execution step
ValidateStep.cs # Built-in validation step
DynamicStep.cs # YAML-driven dynamic step
ForeachStep.cs # Iterates over a collection
ParallelStep.cs # Runs branches concurrently
ParallelForeachStep.cs # Iterates over a collection concurrently
StepBuilder.cs # Builds step trees from YAML model
UI/
PipelineRenderer.cs # Spectre.Console output
ConsoleTheme.cs # Theme constants
Logs are written to the logs/ directory with daily rolling files:
logs/codegenesis-20260225.log
Log level is Debug by default. Logs include timestamps, Claude process invocations, exit codes, and durations.
- Claude Code Documentation
- Claude Code CLI Reference
- Claude Code SDK (Sub-agents)
- Claude Models Overview
- Claude Code Best Practices
- Prompt Engineering Guide
- Anthropic Cookbook
- Spectre.Console Documentation
- YamlDotNet
See CONTRIBUTING.md for guidelines on how to contribute.
This project is licensed under the MIT License. See LICENSE for details.