Skip to main content
OrcBot’s skills system is the interface between the LLM’s reasoning and the outside world. Skills are executable functions that the agent can call to perform actions: search the web, send messages, run commands, manipulate files, and more.

Architecture

Skill Types

1. Core Skills

Purpose: Built-in tools registered at agent startup. Implementation: Agent.ts (lines 908-2500+) — registerInternalSkills() Registration:
this.skills.registerSkill({
  name: 'web_search',
  description: 'Search the web for information. Use this for current events, facts, or research.',
  usage: 'web_search(query)',
  handler: async (args) => {
    const query = args.query;
    const results = await this.browser.search(query);
    return JSON.stringify(results, null, 2);
  },
  isResearch: true,  // Higher repetition budget
  isDeep: true       // Counts as substantive progress
});
Categories:
CategorySkillsDescription
Researchweb_search, browser_navigate, http_fetch, extract_article, download_fileWeb browsing and data retrieval
Communicationsend_telegram, send_whatsapp, send_discord, send_slack, send_emailMulti-channel messaging
Telegram Rich UItelegram_send_buttons, telegram_send_poll, telegram_react, telegram_edit_message, telegram_pin_messageInteractive Telegram features
File Operationsread_file, write_file, send_file, send_voice_noteFile I/O and delivery
Systemrun_command, get_system_info, system_checkShell execution and diagnostics
Memoryrecall_memory, update_user_profile, update_learning, update_journalLong-term storage
RAGrag_ingest, rag_search, rag_ingest_url, rag_list, rag_deleteKnowledge store operations
Orchestrationschedule_task, heartbeat_schedule, spawn_agent, delegate_taskTask scheduling and multi-agent
Configurationmanage_configRuntime config modification
Reasoningdeep_reason, request_supporting_dataExtended reasoning and clarification
Skills Metamanage_skills, create_custom_skill, self_repair_skillSkill lifecycle
Full list: See SKILLS.md

2. Dynamic Plugins

Purpose: Hot-reloadable TypeScript/JavaScript files for custom skills. Location: ~/.orcbot/plugins/*.ts or ./plugins/*.ts Format:
// ~/.orcbot/plugins/weather-api.ts
export default {
  name: 'weather_api',
  description: 'Get weather from OpenWeatherMap API. Use for weather queries.',
  usage: 'weather_api(city)',
  handler: async (args: any, context: any) => {
    const apiKey = context.config.get('openWeatherApiKey');
    const city = args.city;
    const url = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}`;
    
    const response = await fetch(url);
    const data = await response.json();
    
    return `Weather in ${city}: ${data.main.temp}°C, ${data.weather[0].description}`;
  },
  isDeep: true,  // Resets communication cooldown
  isParallelSafe: false  // Can't run in parallel with other tools
};
Loading:
// Automatic at startup
skillsManager.loadPlugins();

// Manual hot reload
skillsManager.loadPlugins();
logger.info('Plugins reloaded');
Hot Reload: OrcBot clears the require cache before each load, so you can edit plugins without restarting:
# 1. Edit plugin
vim ~/.orcbot/plugins/weather-api.ts

# 2. Trigger reload (automatic on file change, or manual via TUI)
orcbot ui Skills Reload Plugins
Safety:
# orcbot.config.yaml
pluginAllowList: [weather_api, custom_tool]  # Only these plugins load
pluginDenyList: [dangerous_plugin]            # Block these plugins
safeMode: true                                # Disable all plugins

3. Agent Skills (SKILL.md)

Purpose: Declarative skill packages following the agentskills.io spec. Location: ~/.orcbot/plugins/skills/*/SKILL.md Format:
---
name: web-scraper
description: Advanced web scraping with Cheerio and Puppeteer. Use for extracting structured data from complex websites.
license: MIT
compatibility: orcbot >= 2.0
allowedTools:
  - browser_navigate
  - http_fetch
  - run_command
orcbot:
  autoActivate: false
  requiredPackages:
    - cheerio
    - puppeteer
  permissions:
    - network
    - browser
  triggerPatterns:
    - scrape.*website
    - extract.*data.*from
---

# Web Scraper Skill

## When to Use

Use this skill when the user asks to:
- Extract structured data from a website (tables, lists, prices)
- Scrape multiple pages following pagination
- Parse complex HTML structures beyond simple article extraction

## Approach

1. First, use `browser_navigate` to inspect the page structure
2. If the page requires JavaScript, use the browser. Otherwise, use `http_fetch` + Cheerio for speed.
3. Identify CSS selectors for the target data
4. Extract and structure the data as JSON
5. Handle pagination by following "next" links

## Example Selectors

```javascript
// Extract product prices
const prices = $('span.price').map((i, el) => $(el).text()).get();

// Extract table rows
const rows = $('table tr').map((i, el) => {
  return $(el).find('td').map((j, cell) => $(cell).text()).get();
}).get();

Error Handling

  • If the page is protected by CAPTCHA, inform the user
  • If selectors fail, try alternative patterns (XPath, data attributes)
  • If rate-limited, suggest adding delays between requests

**Progressive Disclosure:**

Agent skills use a 2-level disclosure system:

**Level 1: Metadata only (always in prompt)**

```xml
<available_skills>
  <skill>
    <name>web-scraper</name>
    <description>Advanced web scraping with Cheerio and Puppeteer...</description>
    <scripts>scrape.ts, pagination.ts</scripts>
    <references>cheerio-guide.md, puppeteer-tips.md</references>
  </skill>
</available_skills>
Level 2: Full instructions (activated on-demand)
// Agent calls activate_skill("web-scraper")
const skill = skillsManager.activateAgentSkill('web-scraper');

// Now the full SKILL.md body is injected into the prompt
Installation:
# From npm package (follows agentskills.io spec)
orcbot skill install firecrawl/cli
# → Resolves to @firecrawl/cli or firecrawl-cli
# → Downloads via npm pack
# → Extracts and discovers SKILL.md files
# → Installs to ~/.orcbot/plugins/skills/

# From GitHub repo
orcbot skill install https://github.com/user/repo

# From local directory
orcbot skill install ./my-skill/

# From .skill (zip) file
orcbot skill install ./my-skill.skill
Skill matching (auto-activation):
// On action start, SkillsManager scans task description
const matched = await skills.classifySkillsWithLLM(taskDescription, llm);
// → Activates matched skills automatically

// Fallback to regex if LLM unavailable
const matched = skills.matchSkillsForTask(taskDescription);

Skill Interface

interface Skill {
  name: string;           // Unique identifier (lowercase, no spaces)
  description: string;    // What it does + when to use it
  usage: string;          // Example call: "web_search(query)"
  handler: (args: any, context?: AgentContext) => Promise<any>;
  
  // Optional metadata flags
  pluginPath?: string;    // Source file (for uninstallation)
  sourceUrl?: string;     // Original URL (if generated)
  isResearch?: boolean;   // Higher repetition budget
  isSideEffect?: boolean; // Subject to deduplication
  isDeep?: boolean;       // Resets communication cooldown
  isDangerous?: boolean;  // Requires confirmation in autonomy
  isElevated?: boolean;   // Requires admin privileges
  isParallelSafe?: boolean; // Can run in parallel
}

Metadata Flags

isResearch:
  • Higher tool repetition budget (10 vs. 5)
  • Used for: web_search, browser_navigate, http_fetch
  • Prevents premature loop detection during research
isSideEffect:
  • Subject to strict deduplication
  • Used for: send_telegram, send_whatsapp, write_file
  • Prevents duplicate messages/writes
isDeep:
  • Resets transparency nudge timer
  • Counts as “substantive progress”
  • Used for: web_search, browser_navigate, run_command, deep_reason
  • Tells termination review: “real work was done”
isDangerous:
  • Requires explicit confirmation in autonomy mode
  • Used for: run_command, delete_file, install_npm_dependency
  • Blocks execution if user approval not granted
isElevated:
  • Requires admin privileges
  • Filtered out for non-admin tasks
  • Used for: run_command, write_file, manage_config, browser_navigate
isParallelSafe:
  • Can be executed in parallel with other parallel-safe tools
  • Used for: read_file, http_fetch (read-only operations)
  • Enables concurrent execution in multi-agent scenarios

Skill Execution Flow

// 1. LLM decides to call a tool
const decision = {
  tools: [
    { name: 'web_search', metadata: { query: 'Paris weather' } }
  ]
};

// 2. Agent looks up skill
const skill = skills.getSkill('web_search');
if (!skill) {
  throw new Error('Skill not found: web_search');
}

// 3. Validate arguments (basic checks)
if (!decision.tools[0].metadata.query) {
  throw new Error('Missing required argument: query');
}

// 4. Execute handler
try {
  const result = await skill.handler(
    decision.tools[0].metadata,
    { browser, config, agent, logger }
  );
  
  // 5. Save observation to memory
  memory.saveMemory({
    id: `${actionId}-step-${step}`,
    type: 'short',
    content: `Observation: Tool ${skill.name} returned ${result}`,
    metadata: { actionId, step, skill: skill.name }
  });
} catch (error) {
  // 6. Save error to memory
  memory.saveMemory({
    id: `${actionId}-step-${step}-error`,
    type: 'short',
    content: `Observation: Tool ${skill.name} FAILED: ${error.message}`,
    metadata: { actionId, step, skill: skill.name, error: true }
  });
}

Tool Definitions (Native Tool Calling)

For LLMs that support native function calling (OpenAI, Google), SkillsManager generates JSON schemas:
interface LLMToolDefinition {
  type: 'function';
  function: {
    name: string;
    description: string;
    parameters: {
      type: 'object';
      properties: Record<string, any>;
      required: string[];
    };
  };
}

// Generated from skill metadata
const toolDefs = skills.getToolDefinitions();
// [
//   {
//     type: 'function',
//     function: {
//       name: 'web_search',
//       description: 'Search the web for information...',
//       parameters: {
//         type: 'object',
//         properties: {
//           query: { type: 'string', description: 'Search query' }
//         },
//         required: ['query']
//       }
//     }
//   },
//   ...
// ]
Passed to LLM:
const response = await llm.callWithTools(prompt, systemPrompt, toolDefs);
// Native response:
// {
//   content: "I'll search for Paris weather.",
//   toolCalls: [
//     { name: 'web_search', arguments: { query: 'Paris weather' } }
//   ]
// }

Skill Context (AgentContext)

All skill handlers receive a context object:
interface AgentContext {
  browser: WebBrowser;       // Playwright browser instance
  config: ConfigManager;     // Runtime configuration
  agent: Agent;              // Full agent instance
  logger: Logger;            // Winston logger
  workerProfile?: WorkerProfileManager; // Multi-agent metadata
  orchestrator?: AgentOrchestrator;     // Worker spawn/coordination
  [key: string]: any;        // Custom extensions
}

// Usage in skill handler:
handler: async (args, context) => {
  const apiKey = context.config.get('myApiKey');
  const userAgent = await context.browser.getUserAgent();
  context.logger.info('Executing custom skill');
  
  // Access agent methods
  await context.agent.pushTask('Follow-up task', 5);
  
  return 'Result';
}

Prompt Injection

Compact mode (after step 1):
AVAILABLE SKILLS:
- web_search(query) — Search the web
- browser_navigate(url) — Visit a URL
- send_telegram(chatId, message) — Send Telegram message
[... 40+ more skills, name + usage only]

AGENT SKILLS:
- web-scraper — Advanced web scraping (use activate_skill to load)
- pdf-extractor — Extract text from PDFs (use activate_skill to load)
Activated skills (after activate_skill called):
AVAILABLE SKILLS:
[... same as above]

ACTIVATED SKILL INSTRUCTIONS:
<skill name="web-scraper">
# Web Scraper Skill

## When to Use
Use this skill when the user asks to:
- Extract structured data from a website
...
</skill>

Skill Discovery

List all skills:
orcbot skill list
# Core Skills (45):
# - web_search: Search the web for information
# - browser_navigate: Visit a URL and extract content
# ...
# 
# Dynamic Plugins (2):
# - weather_api: Get weather from OpenWeatherMap
# - custom_tool: My custom tool
# 
# Agent Skills (3):
# - web-scraper: Advanced web scraping [INACTIVE]
# - pdf-extractor: Extract text from PDFs [ACTIVE]
# - news-finder: Find latest news [INACTIVE]
Inspect skill:
orcbot skill info web-scraper
# Name: web-scraper
# Description: Advanced web scraping with Cheerio and Puppeteer...
# License: MIT
# Required packages: cheerio, puppeteer
# Permissions: network, browser
# Trigger patterns: scrape.*website, extract.*data.*from
# 
# Bundled resources:
# - scripts/scrape.ts
# - scripts/pagination.ts
# - references/cheerio-guide.md
# - references/puppeteer-tips.md

Creating Skills

Option 1: Core Skill (Hardcoded)

Edit Agent.ts and register:
this.skills.registerSkill({
  name: 'my_skill',
  description: 'What it does and when to use it',
  usage: 'my_skill(arg1, arg2)',
  handler: async (args, context) => {
    // Implementation
    return 'Result';
  },
  isDeep: true
});

Option 2: Dynamic Plugin

Create ~/.orcbot/plugins/my-skill.ts:
export default {
  name: 'my_skill',
  description: 'What it does and when to use it',
  usage: 'my_skill(arg1, arg2)',
  handler: async (args: any, context: any) => {
    // Implementation
    return 'Result';
  }
};
Reload plugins via TUI or restart agent.

Option 3: Agent Skill (SKILL.md)

Create directory structure:
~/.orcbot/plugins/skills/my-skill/
├── SKILL.md              # Frontmatter + instructions
├── scripts/
│   └── helper.ts         # Executable scripts
├── references/
│   └── guide.md          # Documentation
└── assets/
    └── example.png       # Images/files
Write SKILL.md:
---
name: my-skill
description: Brief description of what this skill does and when to use it
license: MIT
allowedTools:
  - browser_navigate
  - http_fetch
orcbot:
  autoActivate: false
  triggerPatterns:
    - my.*skill.*pattern
---

# My Skill

## When to Use
...

## Approach
...
Restart agent or use:
orcbot skill discover

Option 4: Self-Generated Plugin (create_custom_skill)

Ask the agent:
User: Create a skill that fetches crypto prices from CoinGecko
Agent: [Uses create_custom_skill to generate ~/.orcbot/plugins/crypto-price.ts]
Agent: Skill created and loaded. You can now use crypto_price(symbol).

Self-Repair

When a plugin fails to load (syntax error, missing dependency), OrcBot auto-triggers repair:
// SkillsManager.ts:296-315
try {
  const plugin = require(pluginPath);
  this.registerSkill(plugin);
} catch (error) {
  logger.error(`Failed to load plugin ${file}: ${error.message}`);
  
  // Push repair task
  agent.pushTask(
    `System Alert: Plugin '${skillName}' failed to compile. Error: ${error.message}. Use self_repair_skill to fix it.`,
    10,  // High priority
    { source: 'system', error: error.message, skillName }
  );
}
The self_repair_skill handler:
  1. Reads the broken plugin file
  2. Analyzes the error (syntax, type, import)
  3. Calls LLM to generate a fix
  4. Writes corrected code
  5. Reloads plugins
  6. Validates the fix

Skill Validation

For Agent Skills (SKILL.md), OrcBot validates against the spec:
orcbot skill validate ~/.orcbot/plugins/skills/my-skill
# ✓ SKILL.md found
# ✓ Name matches directory (my-skill)
# ✓ Description is descriptive (120 chars)
# ✗ ERROR: name contains uppercase letters (must be lowercase)
# ✗ ERROR: description exceeds 1024 chars
Validation rules:
  • Name: lowercase, alphanumeric + hyphens, no consecutive hyphens, 1-64 chars
  • Description: 10-1024 chars
  • Compatibility: 500 chars max
  • Body: < 500 lines (warning only, large skills are valid)

Parallel Execution

Skills marked isParallelSafe: true can run concurrently:
const tools = [
  { name: 'read_file', metadata: { path: 'file1.txt' } },
  { name: 'read_file', metadata: { path: 'file2.txt' } },
  { name: 'http_fetch', metadata: { url: 'https://api.example.com' } }
];

// All are parallel-safe → execute concurrently
const results = await Promise.all(
  tools.map(t => skills.execute(t.name, t.metadata, context))
);
Unsafe for parallel execution:
  • write_file (race conditions)
  • send_telegram (message ordering)
  • run_command (shared stdout)

Configuration

# Skills behavior
safeMode: false                   # Disable dangerous skills
pluginAllowList: []               # Empty = all plugins allowed
pluginDenyList: []                # Block specific plugins by name
compactSkillsPrompt: false        # Use compact format (names only)
skipSimulationForSimpleTasks: true # Skip pre-task simulation

# Skill routing
skillRoutingRules:
  - intent: search
    preferSkills: [web_search, browser_navigate]
  - intent: code
    preferSkills: [run_command, read_file, write_file]

# Plugin paths
pluginsPath: ~/.orcbot/plugins
skillsPath: ./SKILLS.md

# Agent skill auto-activation
agentSkillAutoActivatePatterns:
  - web-scraper: "scrape.*website"
  - pdf-extractor: "extract.*pdf"

Further Reading