Skip to content

Repository files navigation

Morat

An agentic AI tool in C#. One agent loop, many model providers, pluggable tools.

Names. Morat is the product (from the Hebrew morah, “teacher”). Cherub is its guardian system — the virtual filesystem and the web handler that keep file and network access safe. Pre-release builds (v0.1–v1.0) carry the angelic codename angel. The command and binary are morat; internal identifiers (namespaces, the .cherub directory, CHERUB_* variables) keep the Cherub name.

Install (Windows)

From a checkout, in PowerShell:

.\build.ps1      # test + publish to .\artifacts\publish
.\install.ps1    # elevates, ensures the .NET runtime, copies to C:\Program Files\Morat, adds it to PATH

Open a new terminal and run morat setup. .\uninstall.ps1 removes it. For a build that needs no .NET runtime installed, use .\build.ps1 -SelfContained then .\install.ps1 -SkipRuntime.

Running from source

dotnet run --project src/Cherub.Cli -c Release

The first run has no config.cfg, so Cherub asks how you want to run — provider, model, and how to authenticate — and writes your answers. No API key is entered here; you give the name of the environment variable that holds it. Re-run it any time with morat setup.

Or write config.cfg yourself (copy config.cfg.example):

{
  "provider": "ollama",       // anthropic | openai | gemini | mistral | openrouter | ollama | llamacpp | custom
  "model": "llama3.1",
  "baseUrl": "http://localhost:11434/v1",
  "effort": "high",
}

Any setting can be overridden for a single run with the matching environment variable (CHERUB_PROVIDER, CHERUB_MODEL, CHERUB_BASE_URL, CHERUB_EFFORT, …), which takes precedence over the files.

API keys are never stored in config. Either export the provider's own variable (ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY, MISTRAL_API_KEY, OPENROUTER_API_KEY), or name your own with "apiKeyEnv": "MY_KEY" and export that. Ollama and llama.cpp need no key.

Anthropic sign-in

Cherub never uses a credential you didn't give it. By default it uses only an API key you point it at (ANTHROPIC_API_KEY, CHERUB_API_KEY, or the variable named by apiKeyEnv), and if none is set it stops at startup with a clear message — it will not silently use your ant auth login profile or spend against your account.

If you want Cherub to use an Anthropic CLI login (Claude Code or ant auth login), federation, or an ambient token, opt in with "anthropicAuth": "cli" in config.cfg. Check what will actually be used with:

morat auth

Configuration files

File Audience Edited by Format
config.cfg you you JSONX — JSONC + JSONL
.cherub/env.cfg morat and the agent generated flat KEY=VALUE

JSONX means comments and trailing commas are fine, and you can append further objects one per line — later objects override earlier ones key by key, so a local tweak is one appended line rather than an edit:

{ "provider": "anthropic", "effort": "high" }
{ "effort": "low" }   // today only

.cherub/env.cfg is generated from config.cfg and holds nothing that isn't derivable from it, so it is always safe to delete. Cherub regenerates it at startup when it is missing, corrupt, or out of date; to do it yourself:

morat repair-env

Commands

Command Does
morat Start the interactive session.
morat auth Report which credential will be used, and flag common traps.
morat repair-env Rebuild .cherub/env.cfg from config.cfg.

Layout

Project Contains
Cherub.Core Agent loop, transcript, tool contracts, prompts. No dependencies.
Cherub.Anthropic IAgentModel over the Claude Messages API.
Cherub.OpenAI IAgentModel over the OpenAI chat-completions protocol.
Cherub.Shomer The watchman — consent before anything on your machine changes.
Cherub.Tools Built-in tools.
Cherub.Vfs Virtual filesystem, search, the change journal, path redaction.
Cherub.Config JSONX parsing, env.cfg generation, settings resolution.
Cherub.Tui Terminal rendering behind the IAgentConsole seam.
Cherub.Cli Composition root and command dispatch.

Providers

Anthropic uses its native API. Every other supported service speaks the OpenAI chat-completions protocol, so one implementation covers OpenAI, Gemini's compatibility endpoint, Mistral, OpenRouter, Ollama, and llama.cpp. Adding another compatible service is a preset in OpenAIProviders, not a new project.

Only Anthropic has a default model id — for the others, set model. Guessing at another vendor's catalogue would be the same fabrication the agent's own system prompt forbids.

Tools

Tool Does Consent
http_fetch Retrieve a URL
fs_list List a virtual directory
fs_glob Find files by name
fs_grep Search file contents
fs_read Read a file, plus its revision mark
fs_edit Replace an exact piece of a file on /accept
fs_write Write a whole file on /accept
shell_run Run a command (off by default) on approval

http_fetch retrieves URLs through a built-in HttpClient — no dependency on curl or wget being installed. It caps the response size, follows redirects with each hop re-validated, and refuses hosts that resolve into the local network (loopback, RFC1918, link-local including the cloud metadata endpoint) unless http.allowPrivateNetwork is set. The agent picks these URLs, so they are treated as untrusted input.

Add a tool by implementing ITool, or wrap a lambda with DelegateTool, and register it in the ToolRegistry. If it changes anything, implement IWarded and wrap it in WardedTool.

The working copy

The agent edits a likeness of your project, not your project. Its writes land in a working copy — it reads and searches through that copy, so its own changes are there when it looks for them — and nothing reaches your files until you have read the diff and accepted it.

› refactor the config loader

  2 files changed in the working copy (+31 -18) — /diff to review, /accept to apply.

› /diff
› /accept
  applied 2 files to the project.
Command Does
/diff [path] Review the change as a unified diff
/accept [path] Apply it — all of it, or one file
/reject [path] Discard it; the project was never touched

It is an overlay rather than an eager copy, for the same reason a virtual DOM is not a copy of the document: a copy costs the whole tree at startup and goes stale the moment you edit a file in your own editor. Reading through to the real project keeps the two in step — and means that if a file did change underneath, accepting reports a conflict and leaves it alone instead of overwriting work nobody reviewed. See demut.md.

One honest caveat: shell_run executes against the real project, so a build will not see staged edits until you accept them. You are warned at the prompt when that matters.

Consent, undo, and anchored edits

Nothing on your machine changes without you saying so. Reads run silently; a command is put to you with a preview of what it will do, and you can allow it once, allow that kind of call for the session, or refuse. A prompt nobody answers declines — walking away from a running agent is safe. Standing grants are per program, so trusting git is not trusting rm. (Turn the working copy off with workspace.staged: false and file writes are put to you the same way.)

Every write that reaches disk is journalled. /changes lists what the agent has done this session and /undo takes it back — including undoing an undo.

fs_edit changes an exact piece of a file and leaves the rest byte for byte. It requires the revision mark (siman) that fs_read reported, so an edit written against a stale copy is refused rather than silently clobbering newer work — and a model can never quietly drop the parts of a file it did not think to reproduce.

Full detail: shomer, masoret, search, shell.

Grounding

Every run composes GroundedAnswerPrompt into the system prompt: check whether there is enough information, whether the source is trustworthy, and whether the claim can be verified — then answer, state the uncertainty, or decline. No invented facts, citations, URLs, or APIs.

Documentation

Full reference is in DOCS/:

Topic Document
Projects, seams, the agent loop architecture
config.cfg, env.cfg, JSONX configuration
The provider matrix providers
Anthropic sign-in and morat auth authentication
The tool contract and http_fetch tools
The virtual filesystem vfs
The working copy, /diff and /accept demut
Consent, standing grants, /undo shomer
Anchored editing and the siman masoret
fs_glob, fs_grep, the glob syntax search
shell_run and how host paths stay hidden shell
The terminal UI seam tui
Adding a tool / provider / UI extending
Commands and every setting commands
Build, performance, testing building

Tests

dotnet test -c Release

One test file per module, mirroring the source layout.

License

MIT — see LICENSE.

About

No description, website, or topics provided.

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages