> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orcbot.buzzchat.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Skills System

> Extensible tool registry with core skills, dynamic plugins, and SKILL.md agent skills

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

```mermaid theme={null}
flowchart TB
    subgraph Registration["📋 Skill Registration"]
        Core[Core Skills\nRegistered at startup]
        Plugins[Dynamic Plugins\n~/.orcbot/plugins/*.ts]
        AgentSkills[Agent Skills\n~/.orcbot/plugins/skills/*/SKILL.md]
    end

    subgraph Registry["🗂️ SkillsManager"]
        SkillMap[Skills Map\nname → handler]
        AgentSkillMap[Agent Skills Map\nname → metadata + instructions]
        MetaFlags[Metadata Flags\nisElevated, isSideEffect, isDeep]
    end

    Core --> Registry
    Plugins --> HotLoad[Hot Reload\nts-node + require cache clear]
    HotLoad --> Registry
    AgentSkills --> Discovery[Directory Scan\nParse SKILL.md frontmatter]
    Discovery --> Registry

    subgraph Execution["⚙️ Skill Execution"]
        Lookup[1. Lookup skill by name]
        Validate[2. Validate arguments]
        Execute[3. Call handler]
        Result[4. Return result]
    end

    Registry --> Lookup
    Lookup --> Validate
    Validate --> Execute
    Execute --> Result

    subgraph Tools["🔧 Tool Definitions"]
        ToolDefs[Native Tool Calling\nOpenAI/Google function schema]
    end

    Registry --> Tools
    Tools --> LLM[LLM with Tool Calling]
    LLM --> Decision[Decision\n{tools: [...]}]
    Decision --> Execution
```

## Skill Types

### 1. Core Skills

**Purpose:** Built-in tools registered at agent startup.

**Implementation:** `Agent.ts` (lines 908-2500+) — registerInternalSkills()

**Registration:**

```typescript theme={null}
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:**

| Category             | Skills                                                                                                          | Description                          |
| -------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------ |
| **Research**         | web\_search, browser\_navigate, http\_fetch, extract\_article, download\_file                                   | Web browsing and data retrieval      |
| **Communication**    | send\_telegram, send\_whatsapp, send\_discord, send\_slack, send\_email                                         | Multi-channel messaging              |
| **Telegram Rich UI** | telegram\_send\_buttons, telegram\_send\_poll, telegram\_react, telegram\_edit\_message, telegram\_pin\_message | Interactive Telegram features        |
| **File Operations**  | read\_file, write\_file, send\_file, send\_voice\_note                                                          | File I/O and delivery                |
| **System**           | run\_command, get\_system\_info, system\_check                                                                  | Shell execution and diagnostics      |
| **Memory**           | recall\_memory, update\_user\_profile, update\_learning, update\_journal                                        | Long-term storage                    |
| **RAG**              | rag\_ingest, rag\_search, rag\_ingest\_url, rag\_list, rag\_delete                                              | Knowledge store operations           |
| **Orchestration**    | schedule\_task, heartbeat\_schedule, spawn\_agent, delegate\_task                                               | Task scheduling and multi-agent      |
| **Configuration**    | manage\_config                                                                                                  | Runtime config modification          |
| **Reasoning**        | deep\_reason, request\_supporting\_data                                                                         | Extended reasoning and clarification |
| **Skills Meta**      | manage\_skills, create\_custom\_skill, self\_repair\_skill                                                      | Skill lifecycle                      |

**Full list:** See [SKILLS.md](https://github.com/fredabila/orcbot/blob/main/SKILLS.md)

### 2. Dynamic Plugins

**Purpose:** Hot-reloadable TypeScript/JavaScript files for custom skills.

**Location:** `~/.orcbot/plugins/*.ts` or `./plugins/*.ts`

**Format:**

```typescript theme={null}
// ~/.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:**

```typescript theme={null}
// 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:

```bash theme={null}
# 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:**

```yaml theme={null}
# 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](https://agentskills.io) spec.

**Location:** `~/.orcbot/plugins/skills/*/SKILL.md`

**Format:**

````markdown theme={null}
---
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)**

```typescript theme={null}
// Agent calls activate_skill("web-scraper")
const skill = skillsManager.activateAgentSkill('web-scraper');

// Now the full SKILL.md body is injected into the prompt
```

**Installation:**

```bash theme={null}
# 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):**

```typescript theme={null}
// 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

```typescript theme={null}
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

```typescript theme={null}
// 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:

```typescript theme={null}
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:

```typescript theme={null}
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:

```typescript theme={null}
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:**

```bash theme={null}
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:**

```bash theme={null}
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:

```typescript theme={null}
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`:

```typescript theme={null}
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`:

```markdown theme={null}
---
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:

```bash theme={null}
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:

```typescript theme={null}
// 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:

```bash theme={null}
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:

```typescript theme={null}
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

```yaml theme={null}
# 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

* [Agent Loop](./agent-loop.mdx) — How skills are executed
* [Decision Pipeline](./decision-pipeline.mdx) — Skill validation and guardrails
* [Architecture](./architecture.mdx) — Overall system design
* [Agent Skills Spec](https://agentskills.io) — SKILL.md format specification
