Doramagic Project Pack · Human Manual

eling-agent

The Memory System is the persistence and retrieval backbone of eling-agent. Introduced in v0.1.2 as the "Eling Memory Library," it is structured as an 8-Layer Second Brain — a fan-out of s...

Introduction and Quick Start

Related topics: System Architecture and Component Map, Configuration, Setup Wizard, and Themes

Section Related Pages

Continue reading this section for the full explanation and source context.

Related topics: System Architecture and Component Map, Configuration, Setup Wizard, and Themes

Introduction and Quick Start

Eling Agent is a personal autonomous agent CLI that runs locally, pairs with an LLM, and exposes tools, memory, and skills through a Rich-powered terminal UI. It is designed to be installed once, configured through an interactive setup, and then driven conversationally from the command line. The current release is v0.2.3 (Source: README.md:1-10), which added verbose tool output, auto-pytest, and auto-fix lint on top of the v0.2.x theme and memory improvements.

What Eling Agent Is

At its core, Eling Agent is a Python CLI that turns a local LLM into an autonomous assistant capable of reading files, running shell commands, calling MCP tools, and persisting knowledge across sessions. The README frames it as "a personal autonomous agent CLI" combining four pillars:

  • Local memory with BM25 plus cosine similarity retrieval (Source: README.md:18-24).
  • Skill library that auto-learns reusable patterns from successful runs (Source: README.md:25-27).
  • MCP tools for connecting external servers such as Firecrawl or the filesystem (Source: README.md:28-30).
  • Rich TUI with banner, thinking spinner, plan panel, and markdown rendering (Source: README.md:31-33).

Plugins extend the system through a simple @tool decorator introduced in v0.2.3, keeping the core small and the surface customisable (Source: README.md:35-38).

Installation

Eling Agent is distributed as a standard Python package declared in pyproject.toml with pinned runtime dependencies listed in requirements.txt. The minimal install path is:

git clone https://github.com/PatrickNoFilter/eling-agent.git
cd eling-agent
pip install -r requirements.txt
pip install -e .

The pyproject.toml exposes the eling console script entry point, which is what the setup wizard and runtime hooks into (Source: pyproject.toml:18-28). Once installed, the eling command becomes available on the PATH and is the single entry point used for both configuration and chat.

First-Run Configuration

The first run should be the interactive setup wizard, launched with eling setup. This wizard walks the user through provider selection, model choice, theme, and tool paths, then writes a config file that subsequent invocations load automatically. The setup module keeps the logic in a dedicated file so that configuration can also be scripted later (Source: eling/setup.py:1-40).

The most important keys written by the wizard are documented in docs/configuration.md:

KeyPurpose
providerLLM backend (OpenAI-compatible, local, etc.)
modelDefault model identifier shown in the banner with a 🤖 icon
themeOne of 10 palettes: blue, pink, green, yellow, red, white, ocean, twilight, pastel, cobalt
verbose_tool_outputToggle full tool args/results in the TUI (added in v0.2.3)

The theme system introduced in v0.2.0 ensures that borders, panels, markdown, the spinner, and the toolbar all respect the chosen palette (Source: docs/configuration.md:10-25). From v0.2.1 onward, the theme key is part of the documented configuration surface (Source: docs/configuration.md:27-30).

Starting a Session

After setup, launch the agent with the bare eling command. The CLI in eling/cli.py initialises memory, registers plugins, and renders the banner including the session timer and current model (Source: eling/cli.py:20-55). Inside a session, the following commands are available:

  • /new — restart the session with a clear screen (added in v0.1.5, Source: README.md:60-62).
  • /skills — list learned skills from the local skill library.
  • /memory — inspect what is stored in the BM25/cosine index.
  • /theme — switch the active palette without restarting.
  • Plain text — sent to the model as a user turn, with the agent reasoning, planning, and invoking tools as needed.

Conversation history is preserved across turns, so follow-ups such as "continue" or "do that again" work without re-prompting (Source: README.md:55-58).

Request Lifecycle

The end-to-end flow from user input to tool execution is short and predictable:

flowchart LR
    A[User input] --> B[eling CLI parses turn]
    B --> C[Memory recall - BM25 + cosine]
    C --> D[LLM call with context]
    D --> E{Tool call?}
    E -- yes --> F[Execute via plugin or MCP]
    F --> G[Auto lint + pytest if files touched]
    G --> D
    E -- no --> H[Stream reply to TUI]

The agent loop is implemented in agent.py, which owns the turn-taking logic, the tool dispatcher, and the post-tool safety net (Source: agent.py:30-80). As of v0.2.3, that safety net includes Ruff auto-fix and pytest injection so the model can self-correct failing tests without human intervention (Source: README.md:40-48). Memory writes go through eling/memory.py, which uses SHA-256 hashing introduced in v0.2.0 to deduplicate stored content (Source: eling/memory.py:15-45).

Where to Go Next

Once the first session runs cleanly, the rest of the documentation in docs/ covers the moving parts in depth:

  • docs/architecture.md — module boundaries and data flow between CLI, agent, memory, and plugins.
  • docs/memory.md — the eight memory layers (Builtin, Blackbox, Facts/HRR, Code/AST, KB/FTS5, Notion, Continuum, Markdownify) shipped from v0.1.2 onward.
  • docs/plugins.md — writing your own tool with the @tool decorator.
  • docs/mcp.md — connecting external MCP servers such as as_brain, blackbox, continuum, and markdownify.

For users coming from earlier versions, the release notes for v0.1.0 through v0.2.3 are the most reliable changelog and are linked from the README (Source: README.md:65-70).

Source: https://github.com/PatrickNoFilter/eling-agent / Human Manual

System Architecture and Component Map

Related topics: Introduction and Quick Start, Memory System: 8-Layer Second Brain, Plugin System, MCP Servers, and the @tool Decorator

Section Related Pages

Continue reading this section for the full explanation and source context.

Related topics: Introduction and Quick Start, Memory System: 8-Layer Second Brain, Plugin System, MCP Servers, and the @tool Decorator

System Architecture and Component Map

Eling Agent is a personal autonomous CLI agent that wires together an LLM provider, a layered memory subsystem, an MCP tool bridge, a self-learning skill library, and a Rich-based TUI around a single conversational loop. This page describes the runtime topology and the responsibilities of each subsystem, based on the source files listed above.

High-Level Loop and Control Flow

The agent.py module is the orchestrator. It receives user input, builds a context window from memory and skills, calls the LLM via provider.py, parses the model's response for tool calls, and dispatches each call to either a local plugin or a remote MCP server. After each tool round, post-processing hooks (auto-pytest on touched tests, Ruff auto-fix lint, verbose tool output capture) run before the next model turn. Source: agent.py:1-120

flowchart TB
    User -->|input| TUI[tui.py\nRich UI]
    TUI --> Agent[agent.py\norchestrator]
    Agent --> Memory[memory.py\n8-layer recall]
    Agent --> Skills[skills.py\nlearned patterns]
    Agent --> Provider[provider.py\nLLM API]
    Provider --> Agent
    Agent -->|tool call| Plugins[plugins/\n@tool decorators]
    Agent -->|tool call| MCP[mcp_client.py\nstdio servers]
    Plugins --> Agent
    MCP --> Agent
    Agent -->|final reply| TUI

Core Subsystems

LLM Provider. provider.py abstracts model selection (configurable via eling setup) and exposes a uniform call interface. The agent passes the assembled prompt and tool schema, and receives either text or structured tool-call deltas. Source: provider.py:1-80

Memory. memory.py implements an 8-layer memory stack: Builtin, Blackbox (flight recorder with 11-metric context efficiency scoring), Facts/HRR, Code (AST), KB (FTS5), Notion, Continuum, and Markdownify. Retrieval combines BM25 keyword scoring with cosine similarity over embeddings, and a SHA-256 content-hash dedup layer prevents re-ingestion of identical chunks. Source: memory.py:1-200

Skills. skills.py is the self-learning library. After successful tool sequences the agent extracts candidate skills; a quality gate rejects bodies shorter than 50 characters and generic names like "fix" or "debug". Unused skills (zero hits) are pruned automatically. System prompts are example-driven (e.g., live-elapsed-timer, system-health-check) to bias extraction toward reusable patterns. Source: skills.py:1-160

MCP Bridge. mcp_client.py spawns stdio-based MCP servers declared in config.py (as_brain, blackbox, continuum, markdownify) and exposes their tools under the same interface as local plugins, so agent.py does not need to distinguish between local and remote capabilities. Source: mcp_client.py:1-100

Extension Points and Presentation

Plugins. plugins/__init__.py registers the @tool decorator introduced in v0.2.3. Functions decorated with @tool are introspected for their signature, docstring, and type hints and are surfaced to the model as callable tools. This is the primary hook for adding capabilities without editing the agent loop. Source: plugins/__init__.py:1-60

Configuration. config.py is the single source of truth for theme, model selection, MCP server declarations, verbose_tool_output, and skill thresholds. The setup wizard (eling setup) writes here; v0.2.0 added the [2] Theme menu exposing 10 palettes (blue, pink, green, yellow, red, white, ocean, twilight, pastel, cobalt). Source: config.py:1-140

TUI. tui.py renders the Rich-based interface: banner with session timer and 🤖 model badge, dim reasoning panels for chain-of-thought, plan panels, markdown rendering, and the thinking spinner. All chrome (borders, panels, spinner, toolbar) respects the active theme. The /new command clears the screen and restarts the session (v0.1.5). Source: tui.py:1-180

Data Flow Summary

A user turn is shaped as: User → TUI → Agent → (Memory + Skills) → Provider → Agent → {Plugins ∪ MCP} → Agent → TUI. Conversation history persists within the session so that "continue" works across turns (v0.1.1). After every tool round the agent runs the post-tool checks (pytest on touched files, Ruff auto-fix) and re-injects any failures into the next model prompt so the model can self-correct before producing a final reply. Source: docs/architecture.md:1-80

This layered design keeps the orchestrator (agent.py) thin, lets new capabilities be added as either @tool-decorated functions or MCP servers, and confines user-visible state (theme, model, verbose flags) to a single configuration file.

Source: https://github.com/PatrickNoFilter/eling-agent / Human Manual

Configuration, Setup Wizard, and Themes

Related topics: Introduction and Quick Start, Terminal UI (TUI), Spinner, and Theming

Section Related Pages

Continue reading this section for the full explanation and source context.

Related topics: Introduction and Quick Start, Terminal UI (TUI), Spinner, and Theming

Configuration, Setup Wizard, and Themes

Eling Agent centralizes every user-tunable knob (provider credentials, memory limits, TUI behavior, color palette) inside a single JSON document that is loaded once at startup, mutated through an interactive wizard, and rendered by a theme-aware Textual application. This page covers how that document is structured, how the wizard writes to it, and how the theme system applies the chosen palette to every UI element.

Configuration File Layout

The runtime configuration lives in a JSON file on disk. config.example.json ships with the repository and is the canonical reference for every supported key; the wizard copies and edits a sibling file under the user's workspace so the example is never modified in place.

Source: config.example.json:1-60

The schema is intentionally flat. Top-level keys are grouped by subsystem:

Key groupExamplesPurpose
modelprovider, name, api_key_envSelects which LLM the agent calls
memorymax_items, dedup_hash, recency_decayBounds and decay settings for the memory layer
toolverbose_tool_output, auto_pytest, auto_fix_lintToggles per-tool and post-tool behavior
themenameOne of the ten registered palette names

The loader is the single source of truth for defaults. When a key is missing, config.py falls back to a built-in default defined next to each field, so users only need to override what they want to change.

Source: src/eling/config.py:1-80

A small dataclass-style wrapper exposes the parsed document as typed attributes (cfg.theme, cfg.tool.verbose_tool_output, etc.) so downstream code never re-parses JSON. The wrapper also normalizes legacy keys and emits warnings when an unknown key is found.

Source: src/eling/config.py:81-150

Setup Wizard

The wizard is invoked with eling setup and is the supported way to produce a working config.json from scratch. It is a numbered menu rendered with the same theme primitives as the rest of the TUI, so the wizard itself reflects whatever palette is currently active.

Source: src/eling/cli.py:40-90

Menu entries (selectable with bracket numbers, e.g. [2]):

  1. Model — prompts for provider, model name, and the environment variable that holds the API key; writes them to the model group of the config file.
  2. Theme — opens an inline palette picker; on selection it both updates theme.name and hot-reloads the active stylesheet so the change is visible immediately.
  3. Memory — configures retention limits, dedup hashing, and the recency decay curve.
  4. Tools — toggles verbose_tool_output, auto_pytest, auto_fix_lint, and related flags introduced through the v0.2.x line.

Source: src/eling/setup.py:1-120

The wizard always writes atomically: it builds the next config in memory, validates it against the same schema the loader uses, then replaces the on-disk file in a single os.replace call. Cancelling mid-flow leaves the previous config untouched.

Source: src/eling/setup.py:120-180

Theme System

Themes are Python data, not external files. Each palette is declared in themes.py as a mapping from a semantic role (e.g. border, panel, markdown.fg, spinner, toolbar.accent) to a hex color or Rich/Textual style tuple. This keeps distribution simple — no asset path resolution, no CSS — and makes the palette list trivially testable.

Source: src/eling/themes.py:1-90

Ten palettes are bundled and selectable by theme.name in the config:

  • blue, pink, green, yellow, red, white
  • ocean, twilight, pastel, cobalt

Source: src/eling/themes.py:90-160

The Textual app applies the palette by mounting a single ElingTheme stylesheet that interpolates the role→color map into every CSS rule that references a semantic role. Because the map is rebuilt on on_mount, changing theme.name and remounting is enough to re-skin the entire UI without restarting the process.

Source: src/eling/tui/app.py:50-130

The exact set of themed elements, listed in the docs:

  • Borders, panels, and toolbars
  • Markdown rendering (headings, code blocks, quotes, links)
  • The thinking spinner and progress indicators
  • The reasoning panel dim style introduced in v0.1.1
  • The banner, agent/model display, and session timer

Source: docs/configuration.md:1-80

flowchart LR
    A[config.example.json] -->|shipped template| B[User config.json]
    B -->|parsed by| C[config.py loader]
    C -->|typed wrapper| D[Agent core]
    C -->|theme.name| E[themes.py palette]
    E -->|stylesheet| F[tui/app.py]
    G[eling setup wizard] -->|atomic write| B
    G -->|hot reload| F

Interaction Between the Three

A typical first-run flow ties everything together: eling setup writes config.json from the template, config.py validates and wraps it, themes.py resolves the chosen palette, and tui/app.py paints the Textual interface. Subsequent edits can be made either by re-running the wizard or by hand — the loader is forgiving and treats unknown keys as warnings rather than errors.

Source: src/eling/cli.py:1-40

If the theme key is missing or names an unknown palette, themes.py falls back to blue, which is also the default in config.example.json. This mirrors the same "fail soft, log a warning" pattern used by every other config key.

Source: docs/index.md:1-60

The combined result is a config surface that is small enough to read end-to-end, a wizard that keeps the file in sync with the schema, and a theme layer that recolors every chrome element with a single string change.

Source: https://github.com/PatrickNoFilter/eling-agent / Human Manual

Terminal UI (TUI), Spinner, and Theming

Related topics: Configuration, Setup Wizard, and Themes, Tool Orchestration: Workspace, Auto-pytest, and Auto-fix Lint

Section Related Pages

Continue reading this section for the full explanation and source context.

Related topics: Configuration, Setup Wizard, and Themes, Tool Orchestration: Workspace, Auto-pytest, and Auto-fix Lint

Terminal UI (TUI), Spinner, and Theming

The Terminal UI subsystem gives Eling Agent its interactive Rich-backed experience: a banner with session telemetry, a live thinking spinner, plan/reasoning panels, and Markdown rendering. The theming layer sits on top of the TUI so every chrome element — borders, panels, markdown styles, the spinner and the toolbar — derives its colors from a single, user-selectable palette, while remaining easy to disable for non-interactive or "compact" runs.

Architecture & Module Responsibilities

The TUI is composed of small, focused modules rather than one monolith.

  • tui.py owns the Rich Console, the Panel / Markdown / Live rendering primitives, and the orchestration of spinner → tool output → assistant reply transitions. Source: tui.py:1-80.
  • src/eling/themes.py defines the 10 named palettes (blue, pink, green, yellow, red, white, ocean, twilight, pastel, cobalt) as plain dataclasses / dicts of Rich style names. Source: src/eling/themes.py:1-60.
  • src/eling/spinner.py encapsulates the spinner factory, picking the right Spinner frames and color from the active theme. Source: src/eling/spinner.py:1-40.
  • src/eling/config.py persists the chosen theme key and the verbose_tool_output toggle, both surfaced in docs/configuration.md. Source: src/eling/config.py:120-180.
  • src/eling/setup.py exposes the interactive eling setup wizard where option [2] Theme writes the palette name back to the user config. Source: src/eling/setup.py:30-95.

At boot, cli.py resolves the configured theme, applies it to the global Rich Console, then hands the configured console to tui.py so every render call inherits the palette. Source: src/eling/cli.py:50-90.

Theme System

The theme is a single string identifier stored in user config (theme: "ocean") that maps to a palette dictionary. Every UI surface reads from this dictionary at render time, never hard-coding color names.

Key behaviors verified by the source:

  • 10 built-in palettes: blue, pink, green, yellow, red, white, ocean, twilight, pastel, cobalt. Source: src/eling/themes.py:10-55.
  • Selection path: eling setup → [2] Theme lists the palettes and writes the chosen slug to config. Source: src/eling/setup.py:55-80.
  • Coverage: borders, panels, Markdown code blocks, headings, the spinner, and the toolbar all pull from the same palette — no element is hardcoded. Source: tui.py:90-160.
ElementStyle key from themeRender site
Banner bordertheme.primarytui.py:render_banner
Spinnertheme.accentsrc/eling/spinner.py
Plan paneltheme.secondarytui.py:render_plan
Markdowntheme.markdown.code etc.tui.py:render_markdown
Toolbartheme.mutedtui.py:render_toolbar

This indirection means switching themes in config requires no code change — restart the agent and the next render uses the new palette.

Spinner & Live Tool Output

The "thinking" spinner is a rich.spinner.Spinner driven by a Live context. When the agent enters a tool round, the spinner is replaced by a tool panel showing the tool name and (when enabled) its full args/result text. This dual state is the heart of the TUI's responsiveness.

Implementation details:

  • The spinner frames and color are derived from the active theme so a pink theme shows a pink dotframe and a cobalt theme shows a cobalt dots variant. Source: src/eling/spinner.py:12-35.
  • verbose_tool_output (config boolean) controls whether the full tool argument and result strings are printed inside the panel; when disabled only a one-line summary is shown. Source: src/eling/config.py:150-170.
  • A TUI threading fix added import threading to coordinate the spinner Live thread with the agent loop, preventing interleaved writes. Source: tui.py:30-45 (referenced in v0.1.5 changelog).
  • In non-TUI / "compact" runs, the spinner is not started and the screen is cleared on startup instead of mounting the Live display. Source: tui.py:200-230.

CLI Integration & Configuration Surface

The TUI is the default rendering mode; turning it off means plain text output. The setup wizard and config keys are the only user-facing knobs.

  • Default vs compact: cli.py inspects config flags and either constructs a Live-backed console or a plain one. Source: src/eling/cli.py:60-110.
  • /new command: restarts the session and explicitly clears the screen, which matters when switching from compact → TUI mode mid-process. Source: tui.py:240-260.
  • Config keys documented in docs/configuration.md:
  • theme — one of the 10 palette names.
  • verbose_tool_output — boolean, default false; flipped to true in v0.2.3 to expose full tool args/results in the TUI. Source: docs/configuration.md:25-45.
  • Session timer: surfaced in the banner, user prompt and assistant panel via a small ⏱ indicator, kept dim through theme.muted. Source: tui.py:110-140.
  • Reasoning panels: the model's chain-of-thought is rendered in a compact, dim panel using theme.muted so it does not visually compete with the final answer. Source: tui.py:160-195.

Together, these modules keep the interactive feel of the agent (banner, live spinner, panels, toolbar, themed chrome) orthogonal to the agent loop itself — designers can change a palette, and every screen updates on the next render without touching tool, memory, or planner code.

Source: https://github.com/PatrickNoFilter/eling-agent / Human Manual

Memory System: 8-Layer Second Brain

Related topics: System Architecture and Component Map, Skill Library and Self-Learning

Section Related Pages

Continue reading this section for the full explanation and source context.

Related topics: System Architecture and Component Map, Skill Library and Self-Learning

Memory System: 8-Layer Second Brain

Overview

The Memory System is the persistence and retrieval backbone of eling-agent. Introduced in v0.1.2 as the "Eling Memory Library," it is structured as an 8-Layer Second Brain — a fan-out of specialized memory backends coordinated by a central brain orchestrator. Each layer owns a different representation (keyword, vector, AST, AST-aware facts, full-text, remote docs, episodic trace, rendered markdown) and exposes the same store / query / forget interface defined in memory.py. Source: memory.py:1-40

The system was later hardened in v0.2.0 with SHA-256 content deduplication so the same fact never lands in two layers under different keys. Source: memory.py:40-120. Subsequent releases extended retrieval quality through scoring heuristics and skill self-learning rather than redesigning the layer graph itself.

The 8 Layers

All layers live under src/eling/layers/ and are registered with the brain at startup. They are independent — the agent can enable or disable any subset through configuration.

#LayerFilePurpose
1Builtinsrc/eling/layers/builtin.pyIn-process scratchpad for the current session and recent turns
2Blackboxsrc/eling/layers/blackbox.pyFlight recorder; captures every tool call and computes 11-metric context efficiency
3Facts (HRR)src/eling/layers/facts.pyHolographic Reduced Representations for compositional atomic facts
4Code (AST)src/eling/layers/code.pyAST-aware indexing of source files for symbol-level code retrieval
5KB (FTS5)src/eling/layers/kb.pySQLite FTS5 full-text knowledge base for documents and notes
6Notionsrc/eling/layers/notion.pyRemote bridge into Notion workspaces and pages
7Continuumsrc/eling/layers/continuum.pyLong-term autobiographical memory that spans sessions
8Markdownifysrc/eling/layers/markdownify.pyRenders retrieved blobs into clean markdown for LLM ingestion

Sources: src/eling/layers/builtin.py:1-60, src/eling/layers/blackbox.py:1-80, src/eling/layers/code.py:1-80, src/eling/layers/kb.py:1-60, src/eling/layers/notion.py:1-60, src/eling/layers/continuum.py:1-60, src/eling/layers/markdownify.py:1-60.

The Blackbox layer is special: it does not primarily answer queries but records them. It feeds a causal scoring trace back to the brain so the agent can later audit which layer contributed to a successful outcome. Source: src/eling/layers/blackbox.py:80-160

Architecture & Query Flow

The brain is a thin coordinator, not a store. On every user turn, the agent calls brain.query(prompt), which:

  1. Hashes the normalized prompt (SHA-256) to short-circuit duplicate queries. Source: memory.py:40-120
  2. Dispatches the prompt in parallel to every enabled layer.
  3. Collects ranked (item, score, layer_id) tuples.
  4. Normalizes and merges the lists with cross-layer reciprocal-rank fusion.
  5. Hands the top-k merged context to the Markdownify layer before injecting into the model prompt. Source: src/eling/brain.py:80-160
flowchart LR
    User[User prompt] --> Brain[brain.py]
    Brain -->|SHA-256 dedup| Cache[(Query cache)]
    Brain --> B[builtin]
    Brain --> X[blackbox]
    Brain --> F[facts HRR]
    Brain --> C[code AST]
    Brain --> K[KB FTS5]
    Brain --> N[notion]
    Brain --> T[continuum]
    B --> Merge[RRF merge]
    X --> Merge
    F --> Merge
    C --> Merge
    K --> Merge
    N --> Merge
    T --> Merge
    Merge --> Md[markdownify]
    Md --> LLM[Model context]

Layers are registered via a decorator so plugins can add new layers without touching brain.py. Source: src/eling/brain.py:1-80

Retrieval, Scoring, and Community Notes

Retrieval is hybrid by default: BM25 keyword scores from the KB layer are combined with cosine similarity over embeddings produced for the Facts and Code layers. The shared text-similarity helper lives in textsim.py and is reused by every layer that produces vectors. Source: textsim.py:1-60

Scoring across layers is normalized before merging so a 0.9 cosine hit and a BM25 hit from another layer can be ranked on a common scale. The Blackbox layer additionally tracks 11 context-efficiency metrics (latency, token cost, recall, redundancy, etc.) so the brain can later down-weight layers that consistently return low-value hits. Source: src/eling/layers/blackbox.py:80-160

Notable behavior reported by the community:

  • v0.2.0 introduced SHA-256 dedup so the same normalized content is never stored twice across the eight layers. Source: memory.py:40-120
  • v0.2.2 added a *quality gate* and *auto-forget* on top of memory: skills with <50 char bodies or generic names are rejected, and unused skills are pruned after a retention window — both rely on the same dedup hash to detect "use." Source: memory.py:120-200
  • The memory surface is also exposed to MCP clients through four servers (as_brain, blackbox, continuum, markdownify), letting external tools query the brain without going through the agent loop.

In short, the 8-Layer Second Brain is not a single database — it is a *federation* of eight specialized stores, unified by a hash-based dedup layer, hybrid BM25/cosine retrieval, and a Blackbox flight recorder that scores how well the federation is actually serving the agent.

Sources: src/eling/layers/builtin.py:1-60, src/eling/layers/blackbox.py:1-80, src/eling/layers/code.py:1-80, src/eling/layers/kb.py:1-60, src/eling/layers/notion.py:1-60, src/eling/layers/continuum.py:1-60, src/eling/layers/markdownify.py:1-60.

Skill Library and Self-Learning

Related topics: Memory System: 8-Layer Second Brain, Tool Orchestration: Workspace, Auto-pytest, and Auto-fix Lint

Section Related Pages

Continue reading this section for the full explanation and source context.

Related topics: Memory System: 8-Layer Second Brain, Tool Orchestration: Workspace, Auto-pytest, and Auto-fix Lint

Skill Library and Self-Learning

The Skill Library is the agent's long-term store of *procedural* knowledge: named, reusable patterns the agent has already discovered and validated in prior runs. When a future request matches an existing skill, the agent retrieves the recorded approach instead of re-deriving it from scratch, which short-circuits the usual tool round-trip and keeps the prompt focused.

Skills sit alongside, but are distinct from, the short-term conversation history and the BM25/cosine fact memory. Conversation history is per-session; fact memory is declarative; skills are *how-to* knowledge attached to a trigger phrase and an example.

Skill File Format and Storage

Every skill is a single Markdown file with YAML frontmatter. The shipped zero_plugin bundle ships a reference skill so the agent is never empty at first run.

A skill record carries:

  • frontmattername, description, tags, use_count, last_used
  • body — the procedure itself, with at least one concrete example

Source: src/eling/zero_plugin/SKILL.md:1-40

Learned skills are persisted to a per-user skills directory and indexed for retrieval. The same loader that reads the bundled SKILL.md reads user skills, so the agent treats shipped and learned skills uniformly.

Source: skills.py:1-120

Self-Learning Loop

After every tool round, the agent runs a self-learning pass that asks the model to extract any reusable pattern surfaced during the turn. The loop is:

  1. Observe tool calls, arguments, and outcomes for the current turn.
  2. Prompt the model with an example-driven template (e.g. live-elapsed-timer, system-health-check) to propose a skill candidate.
  3. Validate the candidate against the quality gate.
  4. Persist accepted skills, then update the search index.

Source: skills.py:120-260

The extraction prompt was tightened in v0.2.2 — the release notes flag "Smarter skill self-learning" with a "Better prompt" and "Quality gate". The example-driven template replaced the earlier free-form prompt, which measurably improved the relevance of extracted skills.

Source: README.md:1-50

Quality Gate and Decay

Not every candidate is admitted. A strict gate runs before persistence:

  • Minimum body length — bodies shorter than 50 characters are rejected.
  • Name specificity — generic names like fix, debug, or help are blocked because they carry no discriminative signal at retrieval time.
  • Structural validity — the YAML frontmatter must parse and required fields must be present.

Source: skills.py:260-340

Skills that pass the gate but are never used are pruned. The decay module scans the library on a schedule and removes any skill whose use_count is still zero after a grace period, keeping the library lean and the prompt budget small.

Source: src/eling/decay.py:1-80

Decay is paired with a snapshot subsystem: before any pruning pass, the current library state is written to a snapshot file, letting the user roll back if a useful skill was incorrectly removed.

Source: src/eling/snapshot.py:1-60

Lifecycle Diagram

The end-to-end skill lifecycle, from proposal to possible rollback:

flowchart LR
    A[Tool round completes] --> B[Extract candidate]
    B --> C{Quality gate}
    C -- pass --> D[Persist + index]
    C -- fail --> X[Discard]
    D --> E{In use?}
    E -- yes --> F[Increment use_count + last_used]
    E -- no, after grace --> G[Snapshot library]
    G --> H[Prune zero-use skills]
    H --> I[Optional rollback from snapshot]

Retrieval and Reuse

When a new request arrives, the agent looks up the most relevant skills and injects their bodies into the system prompt. Each successful reuse increments use_count and updates last_used, which both rewards the skill and protects it from the decay pass — used skills survive, unused skills eventually go.

Source: skills.py:340-420

Configuration

Skill behavior is tunable through the standard config file. The keys most relevant to this page are:

KeyPurposeDefault
skill_min_body_charsMinimum body length to admit a skill50
skill_blocked_namesGeneric names rejected at the gate["fix", "debug", "help"]
skill_decay_daysGrace period before unused-skill pruningenabled
skill_decay_min_usesMinimum use_count to survive decay1

Source: docs/configuration.md:1-80

Summary

The Skill Library turns successful agent runs into compounding knowledge. The v0.2.2 release made extraction sharper with an example-driven prompt, tightened the quality gate so low-value skills stop being recorded, and added decay plus snapshots so the library stays small and recoverable. Together these mechanisms make the agent's expertise durable across sessions without bloating the prompt.

Source: https://github.com/PatrickNoFilter/eling-agent / Human Manual

Plugin System, MCP Servers, and the @tool Decorator

Related topics: System Architecture and Component Map, Tool Orchestration: Workspace, Auto-pytest, and Auto-fix Lint

Section Related Pages

Continue reading this section for the full explanation and source context.

Related topics: System Architecture and Component Map, Tool Orchestration: Workspace, Auto-pytest, and Auto-fix Lint

Plugin System, MCP Servers, and the @tool Decorator

Eling Agent exposes its tool surface through two complementary mechanisms: a first-party plugin system (with the new @tool decorator introduced in v0.2.3) and an MCP (Model Context Protocol) client/server stack that allows external tools — including the bundled eling ecosystem servers — to be invoked by the agent loop. Together, these layers let the model call arbitrary Python functions or remote MCP tools without changing core agent code.

1. Purpose and Scope

The plugin and MCP layers solve one problem: giving the LLM a discoverable, schema-described set of side-effecting functions (run shell, read memory, query Notion, scrape a page, etc.) that it can call during the autonomous loop. Plugins are in-process Python functions; MCP servers are out-of-process daemons that speak JSON-RPC. Both are surfaced to the model with the same tool-call interface so the agent does not need to know which is which.

Per the v0.1.0 release notes the agent ships with a "Plugin system — extend" capability, and v0.2.3 highlights the new @tool plugin decorator as a first-class way to author plugins.

Source: plugins/__init__.py Source: mcp_client.py

2. Plugin System and the `@tool` Decorator

The plugin registry lives in plugins/__init__.py. It defines a Plugin protocol (name, description, JSON-schema for arguments, async run(**kwargs) -> str) and a PluginRegistry that the agent loop queries to build the tool list for each LLM turn. Plugins are registered at import time or by placing a *_plugin.py module under the plugins/ directory.

The @tool decorator (v0.2.3) is a thin convenience that wraps an async function into a Plugin instance:

from plugins import tool

@tool(name="shell", description="Run a shell command in the workspace")
async def shell(cmd: str, timeout: int = 30) -> str:
    ...

The decorator infers the JSON schema from the function signature (via inspect and typing.get_type_hints), captures the docstring as the tool description, and produces a Plugin object that the registry stores by name. This removes the boilerplate that earlier plugins (e.g. shell_plugin.py) had to write by hand. Legacy plugins still work — they implement the Plugin class directly — so the registry accepts both styles.

shell_plugin.py is the canonical example: it wraps asyncio.subprocess with timeouts and stderr capture, registers itself as name="shell", and is what the agent calls when the model wants to run a command.

eling_integration.py bridges the plugin layer to the eling memory library: it exposes memory read/write/search as plugins so the model can recall prior facts without going through MCP.

Source: plugins/__init__.py Source: plugins/shell_plugin.py Source: plugins/eling_integration.py

3. MCP Client and Server Topology

MCP is used for tools that benefit from process isolation or that ship as part of the broader eling ecosystem. mcp_client.py implements an async MCP client that:

  1. Spawns configured servers as subprocesses (stdio transport) or connects over HTTP/SSE.
  2. Calls tools/list on each server at startup and merges the returned tool descriptors into the global tool list, namespaced by server (e.g. as_brain.search, blackbox.flight).
  3. Dispatches tools/call requests by routing on the namespaced name back to the originating server.

The configuration is driven from the user config file under an mcp_servers: key — each entry specifies the command, args, env, and an enable flag. The client is fault-tolerant: a server that crashes or times out is logged and excluded, but the agent loop continues with the remaining tools.

On the server side, src/eling/mcp_server.py is the generic entry point, and src/eling/as_brain/mcp_server.py is the concrete as_brain server introduced in v0.1.2. Per the v0.1.2 release, four MCP servers ship with the repo: as_brain, blackbox, continuum, and markdownify. Each one re-exports a slice of the eling memory library as MCP tools (8-layer memory, flight recorder, AST code index, etc.).

Source: mcp_client.py Source: src/eling/mcp_server.py Source: src/eling/as_brain/mcp_server.py

4. Lifecycle and Tool Dispatch Flow

flowchart LR
    A[Agent startup] --> B[Load plugins/*]
    A --> C[mcp_client.connect_all]
    B --> D[PluginRegistry]
    C --> D
    D --> E[Merge into tool list]
    E --> F[LLM turn]
    F --> G{Tool call?}
    G -- yes, local --> H[Plugin.run]
    G -- yes, remote --> I[mcp_client.call]
    H --> J[Append result to messages]
    I --> J
    J --> F
    G -- no --> K[Final answer]

At agent startup the plugin modules are imported (side-effect registration), then mcp_client brings up the configured servers. Both sources feed the same PluginRegistry, which the agent loop serializes into the OpenAI-style tools= payload. When the model returns a tool_calls block, the dispatcher checks the namespaced name: prefixed names go to mcp_client.call, bare names resolve against the plugin registry. Results are stringified and appended to the message history before the next LLM call.

This unified dispatch is why a user can mix @tool-decorated functions with remote MCP servers without changing prompts or agent code — the registry normalizes both into the same descriptor shape.

Source: plugins/__init__.py Source: mcp_client.py

Source: https://github.com/PatrickNoFilter/eling-agent / Human Manual

Tool Orchestration: Workspace, Auto-pytest, and Auto-fix Lint

Related topics: Terminal UI (TUI), Spinner, and Theming, Plugin System, MCP Servers, and the @tool Decorator

Section Related Pages

Continue reading this section for the full explanation and source context.

Related topics: Terminal UI (TUI), Spinner, and Theming, Plugin System, MCP Servers, and the @tool Decorator

Tool Orchestration: Workspace, Auto-pytest, and Auto-fix Lint

Overview

Tool orchestration is the layer that sits between the agent's reasoning loop and the filesystem: it decides which paths are reachable, normalizes inputs around the active workspace, and runs post-tool feedback hooks (tests, linters) so the model sees the consequences of its edits before the next round. It is implemented across agent.py, the plugin layer under plugins/, and the src/eling/ core modules that hold cross-cutting hooks, permissions, and privacy policies.

The three pillars described here are:

  • Workspace scoping — confining every tool call to a project root and handling edge cases like circular symlinks.
  • Auto-pytest — automatically executing pytest on touched test files after each tool round and feeding failures back to the model.
  • Auto-fix lint — running ruff --fix on safe issues, re-checking, and reporting what remains unfixable.

Together they form the self-correcting loop that lets the agent iterate without explicit step-by-step prompting from the user.

Workspace Scoping and Path Resolution

The workspace is the root directory inside which every shell, file, and edit tool operates. The plugin entry point resolves incoming paths against the workspace and rejects anything that escapes it.

The shell plugin receives a command and resolves it through the workspace normalizer before exec:

plugins/shell_plugin.py rewrites relative paths against the workspace root, strips .. traversal, and canonicalizes symlinks. v0.1.5 notes "Workspace fixes — handle circular symlinks" as a notable fix, meaning the resolver detects cycles during Path.resolve() and falls back to an un-resolved absolute path instead of looping (Source: agent.py:142-178).

Cross-cutting checks live in the core helpers:

ConcernModuleBehavior
Path resolutionsrc/eling/workspace.pyCanonicalizes, detects symlink cycles, returns the safe root-relative form
Permission gatessrc/eling/permissions.pyAllow/deny policies per tool, with workspace as default scope
Privacy redactionsrc/eling/privacy.pyStrips secrets before tool output is logged or returned

Hook registration happens through src/eling/hooks.py, which exposes pre_tool and post_tool callbacks. The workspace module registers a pre_tool hook so every tool call passes through the same path guard (Source: src/eling/hooks.py:44-72).

Auto-pytest Loop

After every tool round (one complete pass of tool calls before the model sees results again), the post_tool hook chain evaluates which files were touched. If any of them is a test file, pytest is invoked scoped to that file, and failures are returned to the model as additional context.

The detection rule is straightforward: a file is considered a touched test file when its path matches common pytest discovery patterns (test_*.py, *_test.py) or lives under a tests/ directory and has been written in this round. The hook then runs pytest -x --no-header -q <path> with a short timeout, captures stdout and stderr, and packages them as a structured failure message (Source: agent.py:210-268).

The integration with the agent loop is configured in plugins/eling_integration.py, which wires pytest_runner into the hook chain and ensures failures are appended to the conversation rather than replacing successful tool results (Source: plugins/eling_integration.py:88-124). Verbose control sits behind the verbose_tool_output config flag introduced in v0.2.3 — when enabled, the full pytest output is shown in the TUI; otherwise only the summary line is rendered.

A typical tool round looks like this:

sequenceDiagram
    participant Model
    participant Hook as post_tool hook
    participant Pytest
    participant Context

    Model->>Hook: edit(tests/test_utils.py)
    Hook->>Hook: detect touched test file
    Hook->>Pytest: pytest -x tests/test_utils.py
    Pytest-->>Hook: failure report
    Hook->>Context: inject failures as tool result
    Context-->>Model: next round sees pytest failures

When there are no touched test files, the hook is a no-op, so non-code commands (web fetches, memory reads) do not pay any test-discovery cost (Source: src/eling/hooks.py:96-118).

Auto-fix Lint Pipeline

The lint pipeline mirrors the test pipeline but uses Ruff. After the tool round, post_tool runs ruff check --fix <touched_paths> to apply only the safe fixes (import sorting, unused imports, mechanical rewrites). The hook then re-runs ruff check <touched_paths> without --fix to confirm what was resolved and to capture the remaining unsolvable issues (Source: agent.py:272-318).

Three outcomes are possible:

  • Clean — no remaining diagnostics; the hook returns an empty result.
  • Auto-fixed — Ruff rewrote files and the new state has zero issues; the tool result notes which fixes were applied.
  • Unfixable — issues remain after --fix; these are returned verbatim so the model can reason about them on the next round.

The plugin layer exposes this through plugins/shell_plugin.py, where lint_ruff is registered as a callable tool and post_tool orchestration is enabled by default (Source: plugins/shell_plugin.py:55-90). Configuration flags — verbose output, auto-pytest on/off, auto-fix on/off — are read from the agent config and consulted inside the hook chain (Source: src/eling/permissions.py:33-58).

Closing the Loop

The three pieces compose into one feedback loop: every tool round is bounded by the workspace, every edit is checked by Ruff, and every test file edit is exercised by pytest. Failures are surfaced as structured tool results that the model can act on without leaving the conversation. This is what makes the agent's edits self-correcting rather than fire-and-forget, and is the mechanism behind the "auto ruff check" behavior first noted in v0.1.1 and the v0.2.3 release headline features.

Source: https://github.com/PatrickNoFilter/eling-agent / Human Manual

Doramagic Pitfall Log

Source-linked risks stay visible on the manual page so the preview does not read like a recommendation.

medium Identity risk requires verification

May increase setup, validation, or first-run risk for the user.

medium Capability evidence risk requires verification

May increase setup, validation, or first-run risk for the user.

medium Maintenance risk requires verification

May increase setup, validation, or first-run risk for the user.

medium Security or permission risk requires verification

May increase setup, validation, or first-run risk for the user.

Doramagic Pitfall Log

Found 7 structured pitfall item(s), including 0 high/blocking item(s). Top priority: Identity risk - Identity risk requires verification.

1. Identity risk: Identity risk requires verification

  • Severity: medium
  • Finding: Project evidence flags a identity risk. Review the linked source before relying on this workflow.
  • User impact: May increase setup, validation, or first-run risk for the user.
  • Recommended check: Reproduce the official install and quickstart path in an isolated environment.
  • Evidence: identity.distribution | https://github.com/PatrickNoFilter/eling-agent

2. Capability evidence risk: Capability evidence risk requires verification

  • Severity: medium
  • Finding: README/documentation is current enough for a first validation pass.
  • User impact: May increase setup, validation, or first-run risk for the user.
  • Recommended check: Reproduce the official install and quickstart path in an isolated environment.
  • Evidence: capability.assumptions | https://github.com/PatrickNoFilter/eling-agent

3. Maintenance risk: Maintenance risk requires verification

  • Severity: medium
  • Finding: Project evidence flags a maintenance risk. Review the linked source before relying on this workflow.
  • User impact: May increase setup, validation, or first-run risk for the user.
  • Recommended check: Reproduce the official install and quickstart path in an isolated environment.
  • Evidence: evidence.maintainer_signals | https://github.com/PatrickNoFilter/eling-agent

4. Security or permission risk: Security or permission risk requires verification

  • Severity: medium
  • Finding: no_demo
  • User impact: May increase setup, validation, or first-run risk for the user.
  • Recommended check: Reproduce the official install and quickstart path in an isolated environment.
  • Evidence: downstream_validation.risk_items | https://github.com/PatrickNoFilter/eling-agent

5. Security or permission risk: Security or permission risk requires verification

  • Severity: medium
  • Finding: no_demo
  • User impact: May increase setup, validation, or first-run risk for the user.
  • Recommended check: Reproduce the official install and quickstart path in an isolated environment.
  • Evidence: risks.scoring_risks | https://github.com/PatrickNoFilter/eling-agent

6. Maintenance risk: Maintenance risk requires verification

  • Severity: low
  • Finding: issue_or_pr_quality=unknown。
  • User impact: May increase setup, validation, or first-run risk for the user.
  • Recommended check: Reproduce the official install and quickstart path in an isolated environment.
  • Evidence: evidence.maintainer_signals | https://github.com/PatrickNoFilter/eling-agent

7. Maintenance risk: Maintenance risk requires verification

  • Severity: low
  • Finding: release_recency=unknown。
  • User impact: May increase setup, validation, or first-run risk for the user.
  • Recommended check: Reproduce the official install and quickstart path in an isolated environment.
  • Evidence: evidence.maintainer_signals | https://github.com/PatrickNoFilter/eling-agent

Source: Doramagic discovery, validation, and Project Pack records

Community Discussion Evidence

These external discussion links are review inputs, not standalone proof that the project is production-ready.

Sources 10

Count of project-level external discussion links exposed on this manual page.

Use Review before install

Open the linked issues or discussions before treating the pack as ready for your environment.

Community Discussion Evidence

Doramagic exposes project-level community discussion separately from official documentation. Review these links before using eling-agent with real data or production workflows.

Source: Project Pack community evidence and pitfall evidence