CloddsBot Skills System Architecture and Lazy Loading Explained

CloddsBot implements a dual-layer skills architecture that separates prompt-based SKILL.md files from TypeScript handlers, deferring module imports via dynamic import() in src/skills/executor.ts until the first command invocation to minimize startup overhead.

The alsk1992/CloddsBot repository manages 119 bundled skills through a hybrid loading strategy that balances immediate availability with resource efficiency. This design allows the bot to inject static skill definitions into LLM prompts at boot while lazily evaluating executable handlers only when specific commands are triggered.

Dual-Layer Skill Sources

The architecture distinguishes between declarative prompt skills and imperative code handlers, each managed by distinct subsystems within the src/skills/ directory.

SKILL.md Prompt Skills

Static skill definitions reside in SKILL.md files distributed throughout the codebase. These markdown files contain YAML front-matter specifying skill metadata—including name, description, emojis, and gating rules—and are processed by the loader module.

According to the CloddsBot source code, src/skills/loader.ts implements the loadSkillFile() function to eagerly parse these markdown files at startup. The extracted front-matter is injected directly into the LLM system prompt, enabling zero-overhead skill availability without importing executable code. This layer handles passive instruction sets that do not require runtime computation.

TypeScript Handler Modules

Complex skills requiring programmatic logic are implemented as TypeScript modules located at src/skills/bundled/<skill-name>/index.ts. Each module exports a handle(args) function alongside optional command definitions and descriptive metadata.

Unlike prompt skills, these handlers are not loaded during initialization. The src/skills/executor.ts module catalogs available skills by scanning the bundled directory to populate the SKILL_MANIFEST array, but defers the actual import() execution until runtime demand occurs.

Lazy Loading Mechanism

The lazy-loading implementation centers on the initializeSkills() function within src/skills/executor.ts, which orchestrates on-demand module resolution and parallel loading.

Discovery and Manifest Generation

At startup, the executor scans src/skills/bundled/ for subdirectories containing index.ts (or index.js) files. Each valid directory name is appended to the in-memory SKILL_MANIFEST array, creating a registry of available capabilities without evaluating the module code. This discovery phase establishes the routing table used by the command dispatcher.

Dynamic Import Execution

When the first command invoking a TypeScript skill arrives, initializeSkills() triggers the loading sequence. The function executes a Promise.allSettled mapping over SKILL_MANIFEST, initiating dynamic import() calls for each handler module in parallel:

// Conceptual implementation based on src/skills/executor.ts
const SKILL_MANIFEST = ['arbitrage', 'portfolio', 'greet'];

let handlers: Record<string, any> = {};

async function initializeSkills() {
  await Promise.allSettled(
    SKILL_MANIFEST.map(async (name) => {
      try {
        const module = await import(`./bundled/${name}`);
        handlers[name] = module.default ?? module;
      } catch (error) {
        console.error(`Failed to load skill ${name}:`, error);
      }
    })
  );
}

Using Promise.allSettled rather than Promise.all ensures that individual module failures—such as missing dependencies or syntax errors—do not crash the entire skills system. Failed imports are logged and excluded from the active handler cache, allowing the bot to continue operating with partial functionality.

Runtime Execution Flow

Command dispatching follows a check-then-load pattern optimized for subsequent invocations:

  1. Command Receipt – The dispatcher receives a /command invocation and queries the SKILL_MANIFEST for the target skill.
  2. Lazy Initialization Check – If the requested handler is absent from the cache, the system calls initializeSkills() to execute the dynamic imports.
  3. Handler Execution – The resolved module's handle(args) function receives parsed arguments and returns a response or triggers side effects via built-in tools.
  4. Persistent Caching – Successfully loaded handlers remain in memory for the process lifetime, eliminating import overhead on future calls.

This flow ensures that unused skills consume no memory or CPU resources during the bot's operational lifetime.

Hot-Reload and Development Workflow

The system incorporates filesystem watchers monitoring the src/skills/bundled directory for changes. When a developer modifies a handler file, the watcher triggers a reload sequence that re-imports the affected module and refreshes the in-memory handler cache. This hot-reload capability enables rapid iteration without requiring a full bot restart, streamlining the development cycle for new skills.

Summary

  • CloddsBot separates skills into SKILL.md prompt files and TypeScript handlers, managed by src/skills/loader.ts and src/skills/executor.ts respectively.
  • Prompt skills load eagerly at startup, while code skills in src/skills/bundled/ utilize lazy loading via dynamic import() in initializeSkills().
  • The SKILL_MANIFEST array tracks available skills without immediate module evaluation, reducing initial memory footprint.
  • Parallel loading via Promise.allSettled isolates import failures and prevents cascade errors during skill initialization.
  • Hot-reload functionality enables real-time updates to handler logic without service interruption.

Frequently Asked Questions

How does CloddsBot determine when to load a TypeScript skill?

The bot loads TypeScript handlers only upon first invocation of a command mapped to that skill. The initializeSkills() function in src/skills/executor.ts checks the handler cache, and if the module is missing, it executes dynamic import() calls for all entries in the SKILL_MANIFEST array.

What happens if a skill module fails to import?

The system uses Promise.allSettled to wrap each dynamic import, capturing individual rejections without terminating the loading sequence. Errors are logged to the console, and the specific skill becomes unavailable, but the bot continues operating with successfully loaded handlers.

Where are skill handlers physically stored in the repository?

Executable skill handlers reside in src/skills/bundled/<skill-name>/index.ts, where each subdirectory represents a distinct skill. The executor scans this directory structure during startup to build the SKILL_MANIFEST routing table.

Can I create a skill without writing TypeScript code?

Yes. Creating a SKILL.md file with valid YAML front-matter allows you to define prompt-based skills that the loader injects into the LLM context. These skills require no compilation or dynamic import and are available immediately after the loader parses the file at startup.

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 →