Architecture Overview
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ merlin-cli │────▶│ merlin-core │◀───▶│ fledge │
│ (REPL, │ │ (agent loop │ │ (plugins, │
│ streaming, │ │ provider, │ │ lanes, │
│ slash │ │ plugin │ │ spec- │
│ commands) │ │ spec │ │ sync) │
│ │ │ loader) │ │ │
└──────────────┘ └──────────────┘ └──────────────┘
│ │
│ │
▼ ▼
┌──────────────┐ ┌──────────────┐
│ LLM API │ │ Plugins │
│ (Anthropic, │ │ (files, │
│ OpenAI, │ │ search, │
│ Ollama) │ │ shell, │
│ │ │ git, │
│ │ │ specsync, │
│ │ │ memory, │
│ │ │ algochat) │
└──────────────┘ └──────────────┘
Crates
| Crate | Role |
|---|---|
fledge-protocol | The fledge-v1 JSON-lines plugin protocol library. Used by every plugin. |
merlin-core | Agent loop, LLM providers, plugin orchestration, spec-aware planning, memory adapter, AlgoChat adapter. |
merlin-cli | CLI entrypoint, REPL, slash commands, streaming output. |
Plugins
A broad set of plugins ship in-tree under plugins/. Core set:
| Plugin | Commands |
|---|---|
fledge-plugin-files | files-read, files-write, files-edit, files-glob, files-list, files-mkdir, files-stat |
fledge-plugin-search | search-grep, search-references, search-symbols |
fledge-plugin-shell | shell-exec (flagged dangerous) |
fledge-plugin-specsync | specsync-check, specsync-list, specsync-read, specsync-create |
fledge-plugin-git | git-status, git-diff, git-commit, git-branch, git-add, git-checkout, git-stash, git-log |
fledge-plugin-cargo | cargo-build, cargo-test, cargo-clippy, and more |
fledge-plugin-node | node-run, auto-detects npm/bun/pnpm/yarn |
fledge-plugin-vision | Image description via local Ollama |
fledge-plugin-voice | Transcription and synthesis |
fledge-plugin-discord-bridge | Discord bot bridge |
fledge-plugin-telegram-bridge | Telegram bot bridge |
External plugins (memory, algochat, sql, localnet) are picked up
dynamically from fledge plugins list --json.
Boundaries
- No direct LLM calls anywhere except
merlin-core::providers. The CLI never talks to Anthropic; the agent loop never knows it’s dealing with a streaming SSE response. - No direct plugin invocation anywhere except
merlin-core::plugin. The agent loop callsplugin::run_plugin_command(name, &argv); the implementation shells to fledge. - No fledge-specific knowledge in the LLM provider crates.
Providers know about
Message,ContentBlock,ToolDefinition— not about fledge plugins.
Module Specs
The contracts these crates uphold are documented in
specs/ and
verified by fledge spec check as part of the verify lane:
agent— agent state machineprovider— LLM provider traitfledge-protocol— plugin communication protocol
Reading Order
If you’re reading the source for the first time:
crates/merlin-core/src/agent.rs— the central state machine. ~500 lines, organized by// MARK:sections.crates/merlin-core/src/provider.rs— theLlmProvidertrait and shared types.crates/merlin-core/src/plugin.rs—CommandSpec, schema generation, argv translation.crates/merlin-core/src/spec_loader.rs— relevance scoring and section extraction.crates/merlin-cli/src/main.rs— entrypoint, signal handler, event dispatch to output.