# CloddsBot Command System: Handling Native vs Skill-Based Commands

> Explore CloddsBot's unified command system that efficiently handles native and skill-based commands using CommandRegistry for seamless logic and runtime extension management.

- Repository: [AL/CloddsBot](https://github.com/alsk1992/CloddsBot)
- Tags: how-to-guide
- Published: 2026-09-11

---

**CloddsBot routes both hard-coded native commands and dynamically loaded skill commands through a unified CommandRegistry, storing all definitions in a Map<string, CommandDefinition> while separating core logic from runtime extensions.**

The alsk1992/CloddsBot repository implements a dual-layer command architecture that distinguishes between built-in native commands and externally loaded skill-based commands. This design allows the core bot to remain lightweight while supporting runtime extensibility through community-contributed skill packages. Both command types ultimately resolve through the same execution pipeline in [`src/commands/registry.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/commands/registry.ts), presenting users with a seamless slash-command interface.

## Native Commands: Hard-Coded Core Implementation

### Defining Commands in createDefaultCommands

Native commands are statically defined in [`src/commands/registry.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/commands/registry.ts) within the `createDefaultCommands()` function. This factory returns an array of `CommandDefinition` objects, each specifying the command name, description, usage string, and handler implementation.

Core commands include:

- **`/help`** (lines 66‑78): Lists all available commands and categories
- **`/memory`** (lines 80‑88): Interfaces with the optional MemoryService for persistent storage
- **`/bot`** (lines 68‑500): Manages trading bot instances with an extensive handler

```typescript
// src/commands/registry.ts
export interface CommandDefinition {
  name: string;
  description: string;
  usage: string;
  handler: (args: string, context: CommandContext) => Promise<string>;
}

export function createDefaultCommands(): CommandDefinition[] {
  return [
    {
      name: 'help',
      description: 'Display available commands and usage',
      usage: '/help [category]',
      handler: async (args, ctx) => {
        const categories = ctx.registry.listAll();
        return formatHelpOutput(categories, args);
      }
    },
    {
      name: 'memory',
      description: 'Read from MemoryService storage',
      usage: '/memory <key>',
      handler: async (args, ctx) => {
        if (!ctx.memoryService) return 'Memory service unavailable';
        return ctx.memoryService.get(args.trim());
      }
    }
  ];
}

```

### Registration at Boot Time

When the bot initializes, `createCommandRegistry()` instantiates a `CommandRegistry` and calls `registerMany()` with the array from `createDefaultCommands()` (lines 80‑96). The registry stores each command in an internal `Map<string, CommandDefinition>`, enabling O(1) lookup by command name.

```typescript
// src/commands/registry.ts
export class CommandRegistry {
  private commands = new Map<string, CommandDefinition>();
  private aliases = new Map<string, string>();

  registerMany(definitions: CommandDefinition[]): void {
    definitions.forEach(def => {
      this.commands.set(def.name, def);
      // Additional alias resolution logic
    });
  }
  
  createCommandRegistry(): CommandRegistry {
    const registry = new CommandRegistry();
    registry.registerMany(createDefaultCommands());
    return registry;
  }
}

```

### Execution Pipeline

Incoming slash commands are processed by `CommandRegistry.handle()` (lines 121‑150). This method parses the command name, resolves aliases through the internal map, retrieves the corresponding `CommandDefinition`, and invokes its handler within a `try/catch` block for consistent error logging.

```typescript
// src/commands/registry.ts
async handle(message: string, context: CommandContext): Promise<string> {
  try {
    const [commandName, ...argParts] = message.split(' ');
    const resolvedName = this.aliases.get(commandName) ?? commandName;
    const command = this.commands.get(resolvedName);
    
    if (!command) {
      return `Unknown command: ${commandName}`;
    }
    
    return await command.handler(argParts.join(' '), context);
  } catch (error) {
    console.error(`Command execution failed: ${error}`);
    return 'An error occurred while processing your request';
  }
}

```

## Skill-Based Commands: Dynamic Extension Architecture

### Skill Package Structure

Skills are self-contained bundles stored in the `~/.clodds/skills` directory (or a custom path via `config.skillsDir`). Each skill requires a [`SKILL.md`](https://github.com/alsk1992/CloddsBot/blob/main/SKILL.md) file describing sub-commands and an optional [`skill.json`](https://github.com/alsk1992/CloddsBot/blob/main/skill.json) for environment-specific overrides. The loader in [`src/skills/loader.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/skills/loader.ts) (lines 263‑311) parses these files to instantiate a `Skill` object with metadata and command handlers.

```markdown
<!-- ~/.clodds/skills/market-tracker/SKILL.md -->

# Market Tracker Skill

## Commands

### track

- **name**: track
- **description**: Monitor price movements for specific pairs
- **usage**: /track <pair> [interval]

### report

- **name**: report
- **description**: Generate market analysis reports
- **usage**: /report [timeframe]

```

### Discovery and Loading Process

The `SkillManager` class in [`src/skills/index.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/skills/index.ts) manages the skill lifecycle. Its `loadSkills()` method walks the skills directory, calling `loadSkill()` for each [`SKILL.md`](https://github.com/alsk1992/CloddsBot/blob/main/SKILL.md) file encountered and storing the resulting `Skill` objects in memory (lines 91‑104). The [`src/skills/registry.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/skills/registry.ts) module (lines 176‑220) extends this capability to remote registries, handling archive downloads and checksum verification.

```typescript
// src/skills/index.ts
export class SkillManager {
  private skills: Map<string, Skill> = new Map();

  async loadSkills(skillsDir: string): Promise<void> {
    const entries = await fs.readdir(skillsDir, { withFileTypes: true });
    
    for (const entry of entries) {
      if (entry.isDirectory()) {
        const skillPath = path.join(skillsDir, entry.name, 'SKILL.md');
        try {
          const skill = await this.loadSkill(skillPath);
          this.skills.set(skill.meta.name, skill);
        } catch (err) {
          console.warn(`Failed to load skill from ${entry.name}`);
        }
      }
    }
  }
}

```

### Runtime Integration with Command Registry

The agents subsystem in [`src/agents/index.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/agents/index.ts) provides a `reloadSkills()` method that bridges skills with the command system. Invoked during startup or explicit refresh requests, this method iterates over loaded `Skill` objects and registers each sub-command as a `CommandDefinition` with the global `CommandRegistry`.

```typescript
// src/agents/index.ts
async reloadSkills(): Promise<void> {
  await this.skillManager.loadSkills(this.config.skillsDir);
  
  for (const [skillName, skill] of this.skillManager.getSkills()) {
    for (const cmdDef of skill.getCommandDefinitions()) {
      // Wrap skill execution in CommandDefinition format
      this.commandRegistry.registerMany([{
        name: cmdDef.name,
        description: cmdDef.description,
        usage: cmdDef.usage,
        handler: async (args, ctx) => {
          // Generic dispatcher forwards to skill's internal handler
          return skill.handle(cmdDef.name, args, ctx);
        }
      }]);
    }
  }
}

```

### Skill Command Execution Flow

When a user invokes a skill-provided command like `/track`:

1. **Resolution**: The name is resolved by `CommandRegistry.handle()` using the same lookup mechanism as native commands
2. **Dispatch**: The registered handler acts as a generic dispatcher that forwards the argument string to `skill.handle(commandName, args)`
3. **Processing**: The skill's internal implementation executes custom logic (e.g., scanning markets, querying APIs)
4. **Response**: The skill returns a string response that the core bot transmits to the user

## Unified Command Interface and Categorization

### Metadata and UI Integration

Both native and skill commands share the same categorization system. `COMMAND_CATEGORIES` in [`src/commands/registry.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/commands/registry.ts) (lines 65‑78) maps every command name (whether hard-coded or dynamically added) to UI categories such as "Core", "Market Data", or "Virtuals & Agents".

The `listAll()` method (lines 109‑118) constructs a flat array of `{ name, description, category }` objects by iterating over the internal `Map` that contains both native definitions and skill-registered commands. This unified list drives the help interface and command discovery features.

```typescript
// src/commands/registry.ts
const COMMAND_CATEGORIES: Record<string, string[]> = {
  'Core': ['help', 'memory', 'status'],
  'Market Data': ['track', 'report', 'price'],
  'Virtuals & Agents': ['bot', 'agent']
};

listAll(): Array<{name: string, description: string, category: string}> {
  const results = [];
  for (const [name, def] of this.commands) {
    const categories = COMMAND_CATEGORIES[name] ?? ['Uncategorized'];
    results.push({
      name,
      description: def.description,
      category: categories[0]
    });
  }
  return results;
}

```

## Summary

- **Native commands** are hard-coded in [`src/commands/registry.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/commands/registry.ts) via `createDefaultCommands()` and registered at boot time through `registerMany()`, storing definitions in a `Map<string, CommandDefinition>`
- **Skill commands** originate from external bundles in `~/.clodds/skills`, parsed by [`src/skills/loader.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/skills/loader.ts) and integrated into the registry at runtime via [`src/agents/index.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/agents/index.ts) `reloadSkills()`
- **Unified execution** occurs through `CommandRegistry.handle()` (lines 121‑150), which resolves both command types through the same alias resolution and error handling pipeline
- **Metadata merging** happens through `COMMAND_CATEGORIES` and `listAll()` (lines 65‑78, 109‑118), ensuring skill commands appear seamlessly in help listings alongside native functionality
- **Extensibility** requires no core code changes for skills, while native commands demand modification of [`src/commands/registry.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/commands/registry.ts) and redeployment

## Frequently Asked Questions

### Where are native commands defined in the CloddsBot source code?

Native commands are defined in [`src/commands/registry.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/commands/registry.ts) within the `createDefaultCommands()` function (lines 66‑88). This function returns an array of `CommandDefinition` objects containing the command name, description, usage string, and handler implementation. The `/help`, `/memory`, and `/bot` commands are all defined here before being loaded into the registry's internal Map during initialization.

### How does CloddsBot load skill commands at runtime?

Skill commands are loaded through the `SkillManager` class in [`src/skills/index.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/skills/index.ts). The `loadSkills()` method scans the `~/.clodds/skills` directory for [`SKILL.md`](https://github.com/alsk1992/CloddsBot/blob/main/SKILL.md) files (lines 91‑104), parsing each into a `Skill` object. The `reloadSkills()` method in [`src/agents/index.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/agents/index.ts) then registers these with the global `CommandRegistry`, making them available alongside native commands without restarting the core bot process.

### Can skill commands override native commands in CloddsBot?

The `CommandRegistry` uses a `Map<string, CommandDefinition>` to store all commands, and the `registerMany()` method (lines 80‑96) will overwrite existing entries if the same name is registered twice. However, the typical loading sequence registers native commands first, then skills, meaning a skill could theoretically override a native command if it uses an identical name. The system does not enforce name-spacing restrictions between skills and core commands.

### What file structure is required for a CloddsBot skill package?

A valid skill package requires at least a [`SKILL.md`](https://github.com/alsk1992/CloddsBot/blob/main/SKILL.md) file describing the sub-commands and their handlers, located within a subdirectory of `~/.clodds/skills`. Optionally, a [`skill.json`](https://github.com/alsk1992/CloddsBot/blob/main/skill.json) file can provide environment variable overrides or configuration metadata. The loader in [`src/skills/loader.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/skills/loader.ts) (lines 263‑311) parses these files to create the Skill object, while [`src/skills/registry.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/skills/registry.ts) handles remote registry downloads and checksum verification for distributed skill packages.