# CloddsBot Skills System Architecture and Lazy Loading Explained

> Explore CloddsBot's dual-layer skills architecture and lazy loading. Understand how dynamic imports reduce startup overhead for faster command execution.

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

---

**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`](https://github.com/alsk1992/CloddsBot/blob/main/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`](https://github.com/alsk1992/CloddsBot/blob/main/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`](https://github.com/alsk1992/CloddsBot/blob/main/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`](https://github.com/alsk1992/CloddsBot/blob/main/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`](https://github.com/alsk1992/CloddsBot/blob/main/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`](https://github.com/alsk1992/CloddsBot/blob/main/index.ts) (or [`index.js`](https://github.com/alsk1992/CloddsBot/blob/main/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:

```typescript
// 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`](https://github.com/alsk1992/CloddsBot/blob/main/src/skills/loader.ts) and [`src/skills/executor.ts`](https://github.com/alsk1992/CloddsBot/blob/main/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`](https://github.com/alsk1992/CloddsBot/blob/main/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`](https://github.com/alsk1992/CloddsBot/blob/main/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.