# How to Implement Custom Site-Specific Learnings in ego-lite: A Complete Developer Guide

> Learn to implement custom site-specific learnings in ego-lite. This guide shows developers how to create custom AI agent behaviors using manifest.json and JavaScript for targeted site interactions.

- Repository: [CitroLabs/ego-lite](https://github.com/citrolabs/ego-lite)
- Tags: how-to-guide
- Published: 2026-07-27

---

**You implement custom site-specific learnings in ego-lite by creating a folder under `skills/ego-browser/learnings/<site-id>/` containing a [`manifest.json`](https://github.com/citrolabs/ego-lite/blob/main/manifest.json) that declares the site's domains and available tools, plus JavaScript files for node-tools and optional browser-tools that the AI agent can execute.**

ego-lite is an open-source browser automation framework that lets AI agents extend their capabilities through self-contained, site-specific learning packages. These learnings live in the filesystem as declarative modules requiring no core runtime changes, allowing developers to add new site integrations simply by populating a directory with a manifest and tool scripts.

## What Are Site-Specific Learnings in ego-lite?

A **site-specific learning** is a self-contained package that extends the ego-lite agent's browser automation capabilities for a particular domain. Each learning lives under `skills/ego-browser/learnings/<site-id>/` and consists of:

- A **manifest** ([`manifest.json`](https://github.com/citrolabs/ego-lite/blob/main/manifest.json)) declaring domains, tool definitions, and metadata
- **Node-tools** (`tools/*.js`) executed in the Node.js runtime side of the agent
- **Browser-tools** (`browser-tools/*.js`) executed inside the actual browser page context
- **Documentation** (`notes/*.md`) for human reference and the `help()` command

Because the architecture is manifest-driven, you can add new sites without modifying the core runtime—only the new learning folder and its files are required.

## The Learning Subsystem Architecture

When the ego-lite runtime starts, it automatically discovers and registers all learnings through a three-phase process:

### Discovery Phase

The `learningsRoot()` function in [[`src/learning/check-domain-learning.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/learning/check-domain-learning.ts)](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/learning/check-domain-learning.ts) returns the absolute path to `skills/ego-browser/learnings`. The system walks this directory to find all available site folders.

### Validation Phase

Each discovered [`manifest.json`](https://github.com/citrolabs/ego-lite/blob/main/manifest.json) undergoes schema validation via `validateLearnings()` in [[`src/learning/validate-learning-format.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/learning/validate-learning-format.ts)](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/learning/validate-learning-format.ts). Required fields include `id`, `name`, `domains`, and either `nodeTools` or `browserTools`. Malformed manifests emit clear errors preventing runtime loading.

### Indexing Phase

The `loadLearnings()` function in [[`src/learning/index.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/learning/index.ts)](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/learning/index.ts) reads every valid manifest and builds lookup tables that helper functions later use to resolve tool calls.

### Execution Phase

When an agent script invokes `runSiteTool()`, the helper layer in [[`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts)](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/helpers.ts) resolves the request, loads the referenced JavaScript file, and executes it. Node-tools are required as modules (`require(path)`), while browser-tools are sent to the page via `await browserFetch` and executed with `await js(...)`.

## Step-by-Step: Creating a Custom Learning

Follow these seven steps to implement a new site-specific learning:

1. **Create the learning directory** under `skills/ego-browser/learnings/` using a unique ID (e.g., `my-site`).

2. **Add the manifest** at [`skills/ego-browser/learnings/my-site/manifest.json`](https://github.com/citrolabs/ego-lite/blob/main/skills/ego-browser/learnings/my-site/manifest.json) to declare domains and expose tools.

3. **Write node-tool scripts** in `tools/`. Each file must export a single async function matching the `callable` name defined in the manifest.

4. **(Optional) Write browser-tool scripts** in `browser-tools/`. These execute inside the browser context for DOM manipulation.

5. **Add markdown notes** in `notes/` to describe the learning for humans and the `help()` command.

6. **Validate the learning** by running `npm run validate:site-skills` or `npm test`, which exercises the validation code in `validateLearnings`.

7. **Use the tool** from an agent script via `await runSiteTool('my-site', '<tool-id>', args)`.

## Code Implementation Examples

### Minimal Manifest Configuration

Create [`skills/ego-browser/learnings/my-site/manifest.json`](https://github.com/citrolabs/ego-lite/blob/main/skills/ego-browser/learnings/my-site/manifest.json):

```json
{
  "id": "my-site",
  "name": "My Site",
  "domains": ["mysite.com", "*.mysite.com"],
  "notes": ["notes/overview.md"],
  "nodeTools": {
    "extract_info": {
      "description": "Extract title and description from the current page.",
      "path": "tools/extract-info.js",
      "callable": "extractInfo",
      "args": {},
      "returns": {
        "type": "object",
        "description": "Object with `title` and `description` strings."
      }
    }
  }
}

```

*Reference:* See the Google learning manifest at [[`skills/ego-browser/learnings/google/manifest.json`](https://github.com/citrolabs/ego-lite/blob/main/skills/ego-browser/learnings/google/manifest.json)](https://github.com/citrolabs/ego-lite/blob/main/skills/ego-browser/learnings/google/manifest.json) for a production example.

### Node-Tool Implementation

Create [`skills/ego-browser/learnings/my-site/tools/extract-info.js`](https://github.com/citrolabs/ego-lite/blob/main/skills/ego-browser/learnings/my-site/tools/extract-info.js):

```javascript
/** @type {import('..').NodeToolFn} */
module.exports = async function extractInfo (_, { cliLog }) {
  // Use the browser-side helper to get the page's title.
  await cliLog('Running js to fetch title...');
  const title = await cliLog(await js(String.raw`document.title`));

  // Grab the meta description (if any).
  const description = await cliLog(
    await js(String.raw`
      const meta = document.querySelector('meta[name="description"]');
      meta ? meta.content : ''
    `)
  );

  return { title, description };
};

```

When the agent calls `runSiteTool('my-site', 'extract_info')`, ego-lite loads this module and executes the exported `extractInfo` function.

### Browser-Tool Implementation

Create [`skills/ego-browser/learnings/my-site/browser-tools/get-data.js`](https://github.com/citrolabs/ego-lite/blob/main/skills/ego-browser/learnings/my-site/browser-tools/get-data.js):

```javascript
// This script runs inside the page context.
export async function getData () {
  const items = [...document.querySelectorAll('.item')].map(el => ({
    text: el.innerText,
    href: el.querySelector('a')?.href || null,
  }));
  return items;
}

```

Expose it in the manifest under `browserTools`:

```json
"browserTools": {
  "get_page_items": {
    "description": "Return a list of visible items on the page.",
    "path": "browser-tools/get-data.js",
    "args": {}
  }
}

```

Agents invoke this with `await runBrowserTool('my-site', 'get_page_items')`.

### Calling Tools from Agent Scripts

Use the learning in a heredoc executed with `ego-browser nodejs`:

```javascript
const task = await useOrCreateTaskSpace('my-site data extraction');
await openOrReuseTab('https://mysite.com/some-page', { wait: true });

const data = await runSiteTool('my-site', 'extract_info');
cliLog('Extracted data:');
cliLog(JSON.stringify(data, null, 2));

```

## Key Source Files Reference

| File | Purpose |
|------|---------|
| `skills/ego-browser/learnings/<site>/manifest.json` | Declarative description of a learning (domains, tools, notes). Example: [google/manifest.json](https://github.com/citrolabs/ego-lite/blob/main/skills/ego-browser/learnings/google/manifest.json) |
| [`package/ego-browser/src/learning/check-domain-learning.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/learning/check-domain-learning.ts) | Determines the filesystem root of all learnings via `learningsRoot()`. |
| [`package/ego-browser/src/learning/validate-learning-format.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/learning/validate-learning-format.ts) | Validates each [`manifest.json`](https://github.com/citrolabs/ego-lite/blob/main/manifest.json) against the required schema via `validateLearnings()`. |
| [`package/ego-browser/src/learning/index.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/learning/index.ts) | Loads all learnings at startup and builds lookup tables via `loadLearnings()`. |
| [`package/ego-browser/src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/helpers.ts) | Exposes the public helper surface (`runSiteTool`, `runBrowserTool`, etc.) to agent scripts. |

## Summary

- **Site-specific learnings** are self-contained packages under `skills/ego-browser/learnings/<site-id>/` that extend ego-lite without modifying core code.
- The **discovery system** automatically finds learnings at startup, validates their manifests, and registers their tools.
- **Node-tools** run in the Node.js context and can invoke browser-side code, while **browser-tools** execute directly inside the page.
- Required components include a valid [`manifest.json`](https://github.com/citrolabs/ego-lite/blob/main/manifest.json) (with `id`, `name`, `domains`, and tool definitions) and JavaScript files exporting async functions.
- Validation via `npm run validate:site-skills` ensures manifests meet the schema before runtime.

## Frequently Asked Questions

### What is the difference between node-tools and browser-tools in ego-lite?

**Node-tools** execute in the Node.js runtime side of the agent and can access the full server-side environment, including the ability to call `browserFetch` or `js()` to interact with the page. **Browser-tools** are JavaScript files sent directly to the browser page context via `await js()` and execute within the page's sandbox, making them ideal for DOM queries that need access to the page's global state or elements.

### Where does ego-lite store custom site-specific learnings?

According to the source code in [`src/learning/check-domain-learning.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/learning/check-domain-learning.ts), ego-lite resolves learnings to `skills/ego-browser/learnings/` relative to the installation root. Each learning must be a subdirectory named with a unique site ID, containing a [`manifest.json`](https://github.com/citrolabs/ego-lite/blob/main/manifest.json) and optional `tools/` and `browser-tools/` directories.

### How does ego-lite validate custom learning manifests?

The `validateLearnings()` function in [`src/learning/validate-learning-format.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/learning/validate-learning-format.ts) ensures every [`manifest.json`](https://github.com/citrolabs/ego-lite/blob/main/manifest.json) contains required fields: `id`, `name`, `domains`, and tool definitions (`nodeTools` or `browserTools`). If validation fails, the system emits a clear error indicating the offending field, preventing the malformed learning from loading at runtime.

### Can I use wildcards in domain patterns for site-specific learnings?

Yes. The `domains` array in the manifest supports wildcard patterns such as `*.mysite.com` to match subdomains. When the agent navigates to a URL, ego-lite matches the hostname against these patterns to determine which learning's tools are available for that session.