How Site-Specific Learnings Are Loaded from the Skills Directory in ego-lite

Ego-lite loads site-specific learnings (notes, tool definitions, and manifests) from skills/ego-browser/learnings/ at runtime through a dedicated learning subsystem that discovers, validates, and executes domain-specific capabilities.

The ego-lite open-source browser automation framework extends agent capabilities through modular site skills. These learnings live in a structured directory tree and are dynamically discovered based on the current URL, letting agents access domain knowledge and custom tools without hardcoding site logic into the core engine.

Discovery: How ego-lite Finds Relevant Site Skills

The entry point for loading site-specific learnings is siteSkillsForUrl() in package/ego-browser/src/learning/index.ts. This function resolves the site-skills root from the agent workspace and searches for matching manifests.

// From package/ego-browser/src/learning/index.ts
export async function siteSkillsForUrl(url, { agentWorkspace }) {
  const siteSkillsRoot = path.join(agentWorkspace, 'skills', 'ego-browser', 'learnings');
  // ...looks for a manifest whose domain matches the URL
}

The discovery chain works as follows:

  • siteSkillsForUrl() – public API exposed via the site helper
  • siteSkillsForUrlCore() – core implementation that scans siteSkillsRoot
  • iterLearningDirs() – traverses the skills/ego-browser/learnings/ directory
  • loadLearningManifest() – parses each <site>/manifest.json

Domain matching logic in src/learning/check-domain-learning.ts determines whether a site's manifest applies to the current URL based on domain patterns declared in the manifest.

Manifest Loading: The Structure of Site Learnings

Each site skill resides in its own subdirectory with a manifest.json file that declares available capabilities. The manifest schema includes:

  • id and name – site identifier and display name
  • nodeTools – paths to Node.js executable scripts
  • browserTools – paths to browser-executed scripts
  • notes – markdown files containing domain knowledge

Example manifest structure from skills/ego-browser/learnings/x-com/manifest.json:

{
  "id": "x-com",
  "name": "X (Twitter)",
  "domain": "x.com",
  "nodeTools": ["tools/search-users.js"],
  "browserTools": ["tools/extract-timeline.js"],
  "notes": ["notes/rate-limits.md", "notes/api-conventions.md"]
}

Validation occurs through src/learning/validate-learning-format.ts, which ensures manifests conform to the expected schema before loading proceeds.

Context Assembly: Building the LearnedContext Object

Once a matching manifest is found, loadLearnedContext() (exposed as site.learnContext) assembles all relevant information into a structured LearnedContext object.

For each site entry, the system:

  1. Reads every note file under <site>/notes/*.md via fs.readFile
  2. Constructs tool signatures for both Node and browser tools
  3. Attaches ready-to-run examples using await site.runTool(...) or await site.runBrowserTool(...)

The resulting LearnedContext contains:

Property Description
exists Boolean indicating whether a skill was found
siteId, siteName, domain Identification metadata
knowledge Array of { siteId, fileName, content } objects from note files
tools Array of { siteId, toolName, toolType, description, args, returns, example } objects
// Using the loaded context in an ego-browser script
const ctx = await site.learnContext();   // Uses current page URL
if (ctx.exists) {
  console.log('Site:', ctx.siteName);
  console.log('Notes:', ctx.knowledge.map(k => k.fileName));
  console.log('Available tools:', ctx.tools.map(t => t.toolName));
}

Tool Execution: Running Node and Browser Tools

The learning subsystem supports two execution environments for site tools.

Node Tools: Server-Side Execution

runNodeSiteTool() in src/learning/index.ts handles Node-side tool execution:

// Executes skills/ego-browser/learnings/x-com/tools/search-users.js
const result = await site.runTool('x-com', 'search-users', {
  query: 'john doe'
});
console.log(result);

Implementation details from the source:

  • Locates tool file using relativeSitePath()
  • Dynamically imports the module
  • Retrieves the declared callable function
  • Invokes it with the helper context

Browser Tools: In-Page Execution

runSiteBrowserTool() loads and executes code inside the actual browser page:

// Runs a browser-side extraction tool
await site.runBrowserTool('google', 'search-extract', {
  query: 'openai gpt-4'
});

The execution flow:

  • loadBrowserToolSource() fetches raw source as a string
  • wrapBrowserTool() adds necessary wrappers for the execution environment
  • evaluate() injects and runs the code in the page context

Helper Facade: The site Namespace

All learning functionality is exposed through the helper context defined in src/helpers.ts. The createSiteFacade() function injects the site namespace into agent scripts:

export function helperContext(extra = {}) {
  return {
    page: createPageFacade(),
    browser: createBrowserFacade(),
    site: {
      skills: siteSkills,
      skillsForUrl: siteSkillsForUrl,
      runTool: runSiteTool,
      runBrowserTool: runSiteBrowserTool,
      learnContext,
    },
    // …other helpers
  };
}

This facade pattern ensures agents interact with a clean, promise-based API regardless of the underlying file system operations and module loading complexity.

Complete Workflow Example

// Full workflow: discover, load context, and execute tools
async function interactWithSite(url) {
  // Discovery phase
  const skills = await site.skillsForUrl(url);
  console.log('Matching site IDs:', skills.map(s => s.id));

  // Context loading
  const ctx = await site.learnContext();
  if (!ctx.exists) {
    console.log('No site-specific learning available');
    return;
  }

  // Tool execution based on available capabilities
  if (ctx.tools.some(t => t.toolName === 'search-users')) {
    const users = await site.runTool(ctx.siteId, 'search-users', { query: 'ego-lite' });
    console.log('Found users:', users);
  }
}

Key Files in the Learning Subsystem

Purpose File Path
Core learning API (loadLearnedContext, runNodeSiteTool, loadBrowserToolSource) package/ego-browser/src/learning/index.ts
Manifest validation and schema utilities package/ego-browser/src/learning/validate-learning-format.ts
Domain matching and directory traversal package/ego-browser/src/learning/check-domain-learning.ts
Helper facade with site namespace package/ego-browser/src/helpers.ts
Example manifest (X/Twitter) skills/ego-browser/learnings/x-com/manifest.json
Example Node tool skills/ego-browser/learnings/x-com/tools/search-users.js
Example Browser tool skills/ego-browser/learnings/google/tools/search-extract.js

Summary

  • Discovery starts at siteSkillsForUrl(), resolving the skills directory from state.agentWorkspace() and matching manifests by domain
  • Manifest loading via loadLearningManifest() parses manifest.json files declaring tools and notes
  • Context assembly in loadLearnedContext() produces structured LearnedContext objects with knowledge arrays and tool signatures
  • Dual execution paths support Node tools (runNodeSiteTool) and browser tools (loadBrowserToolSourcewrapBrowserToolevaluate)
  • Helper facade exposes all functionality through the site namespace injected into agent scripts

Frequently Asked Questions

Where does ego-lite look for site skills by default?

The default skills root resolves to skills/ego-browser/learnings/ relative to the agent workspace returned by state.agentWorkspace(). This path is constructed in siteSkillsForUrl() using path.join(agentWorkspace, 'skills', 'ego-browser', 'learnings').

What happens if multiple site manifests match a URL?

The siteSkillsForUrl() implementation returns an array of matching entries, allowing multiple skills to apply to overlapping domains. The loadLearnedContext() function aggregates knowledge and tools from all matches, with siteId disambiguating sources.

Can I use site tools without loading the full context?

Yes. While site.learnContext() provides comprehensive discovery, you can call site.runTool() or site.runBrowserTool() directly with explicit site and tool names. These methods internally resolve paths using the same siteSkillsForUrl() discovery logic without requiring a separate context load.

How are browser tools sandboxed during execution?

Browser tools are loaded as raw source strings via loadBrowserToolSource(), wrapped by wrapBrowserTool() to establish the execution environment, and then evaluated through the generic evaluate() helper. This runs within the page context but does not have direct access to the Node.js host environment, maintaining isolation between the agent runtime and the target page.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →