From 0905d978a7f7b10328f7241d247afc82ca3603c4 Mon Sep 17 00:00:00 2001
From: ponderingdemocritus <ponderingdemocritus@protonmail.com>
Date: Sat, 2 Nov 2024 13:52:35 +1100
Subject: [PATCH 1/2] clean up index

---
 core/src/cli/index.ts                         | 205 ++++++++++++++
 .../clients/telegram/src/messageManager.ts    |   1 +
 core/src/index.ts                             | 259 ++----------------
 core/src/types/index.ts                       |   8 +
 4 files changed, 234 insertions(+), 239 deletions(-)
 create mode 100644 core/src/cli/index.ts
 create mode 100644 core/src/types/index.ts

diff --git a/core/src/cli/index.ts b/core/src/cli/index.ts
new file mode 100644
index 00000000000..d6f21138b06
--- /dev/null
+++ b/core/src/cli/index.ts
@@ -0,0 +1,205 @@
+import defaultCharacter from "../core/defaultCharacter.ts";
+import settings from "../core/settings.ts";
+import { Character, IAgentRuntime, ModelProvider } from "../core/types.ts";
+import * as Action from "../actions/index.ts";
+import * as Client from "../clients/index.ts";
+import * as Adapter from "../adapters/index.ts";
+import * as Provider from "../providers/index.ts";
+import yargs from "yargs";
+import { wait } from "../clients/twitter/utils.ts";
+
+import fs from "fs";
+import Database from "better-sqlite3";
+import { AgentRuntime } from "../core/runtime.ts";
+import { defaultActions } from "../core/actions.ts";
+import { Arguments } from "../types/index.ts";
+
+export async function initializeClients(
+    character: Character,
+    runtime: IAgentRuntime
+) {
+    const clients = [];
+    const clientTypes =
+        character.clients?.map((str) => str.toLowerCase()) || [];
+
+    if (clientTypes.includes("discord")) {
+        clients.push(startDiscord(runtime));
+    }
+
+    if (clientTypes.includes("telegram")) {
+        const telegramClient = await startTelegram(runtime, character);
+        if (telegramClient) clients.push(telegramClient);
+    }
+
+    if (clientTypes.includes("twitter")) {
+        const twitterClients = await startTwitter(runtime);
+        clients.push(...twitterClients);
+    }
+
+    return clients;
+}
+
+export function parseArguments(): Arguments {
+    try {
+        return yargs(process.argv.slice(2))
+            .option("character", {
+                type: "string",
+                description: "Path to the character JSON file",
+            })
+            .option("characters", {
+                type: "string",
+                description:
+                    "Comma separated list of paths to character JSON files",
+            })
+            .option("telegram", {
+                type: "boolean",
+                description: "Enable Telegram client",
+                default: false,
+            })
+            .parseSync() as Arguments;
+    } catch (error) {
+        console.error("Error parsing arguments:", error);
+        return {};
+    }
+}
+
+export function loadCharacters(charactersArg: string): Character[] {
+    const characterPaths = charactersArg?.split(",").map((path) => path.trim());
+    const loadedCharacters = [];
+
+    if (characterPaths?.length > 0) {
+        for (const path of characterPaths) {
+            try {
+                const character = JSON.parse(fs.readFileSync(path, "utf8"));
+                loadedCharacters.push(character);
+            } catch (e) {
+                console.error(`Error loading character from ${path}: ${e}`);
+            }
+        }
+    }
+
+    if (loadedCharacters.length === 0) {
+        console.log("No characters found, using default character");
+        loadedCharacters.push(defaultCharacter);
+    }
+
+    return loadedCharacters;
+}
+
+export function getTokenForProvider(
+    provider: ModelProvider,
+    character: Character
+) {
+    switch (provider) {
+        case ModelProvider.OPENAI:
+            return (
+                character.settings?.secrets?.OPENAI_API_KEY ||
+                settings.OPENAI_API_KEY
+            );
+        case ModelProvider.ANTHROPIC:
+            return (
+                character.settings?.secrets?.CLAUDE_API_KEY ||
+                settings.CLAUDE_API_KEY
+            );
+    }
+}
+export function initializeDatabase() {
+    if (process.env.POSTGRES_URL) {
+        return new Adapter.PostgresDatabaseAdapter({
+            connectionString: process.env.POSTGRES_URL,
+        });
+    } else {
+        return new Adapter.SqliteDatabaseAdapter(new Database("./db.sqlite"));
+    }
+}
+
+export function createAgentRuntime(
+    character: Character,
+    db: any,
+    token: string
+) {
+    return new AgentRuntime({
+        databaseAdapter: db,
+        token,
+        modelProvider: character.modelProvider,
+        evaluators: [],
+        character,
+        providers: [Provider.timeProvider, Provider.boredomProvider],
+        actions: [
+            ...defaultActions,
+            Action.askClaude,
+            Action.followRoom,
+            Action.unfollowRoom,
+            Action.unmuteRoom,
+            Action.muteRoom,
+            Action.imageGeneration,
+        ],
+    });
+}
+
+export function createDirectRuntime(
+    character: Character,
+    db: any,
+    token: string
+) {
+    return new AgentRuntime({
+        databaseAdapter: db,
+        token,
+        modelProvider: character.modelProvider,
+        evaluators: [],
+        character,
+        providers: [Provider.timeProvider, Provider.boredomProvider],
+        actions: [...defaultActions],
+    });
+}
+
+export function startDiscord(runtime: IAgentRuntime) {
+    return new Client.DiscordClient(runtime);
+}
+
+export async function startTelegram(
+    runtime: IAgentRuntime,
+    character: Character
+) {
+    console.log("🔍 Attempting to start Telegram bot...");
+    const botToken = runtime.getSetting("TELEGRAM_BOT_TOKEN");
+
+    if (!botToken) {
+        console.error(
+            `❌ Telegram bot token is not set for character ${character.name}.`
+        );
+        return null;
+    }
+
+    try {
+        const telegramClient = new Client.TelegramClient(runtime, botToken);
+        await telegramClient.start();
+        console.log(
+            `✅ Telegram client successfully started for character ${character.name}`
+        );
+        return telegramClient;
+    } catch (error) {
+        console.error(
+            `❌ Error creating/starting Telegram client for ${character.name}:`,
+            error
+        );
+        return null;
+    }
+}
+
+export async function startTwitter(runtime: IAgentRuntime) {
+    console.log("Starting Twitter clients...");
+    const twitterSearchClient = new Client.TwitterSearchClient(runtime);
+    await wait();
+    const twitterInteractionClient = new Client.TwitterInteractionClient(
+        runtime
+    );
+    await wait();
+    const twitterGenerationClient = new Client.TwitterGenerationClient(runtime);
+
+    return [
+        twitterInteractionClient,
+        twitterSearchClient,
+        twitterGenerationClient,
+    ];
+}
diff --git a/core/src/clients/telegram/src/messageManager.ts b/core/src/clients/telegram/src/messageManager.ts
index c48b54a1a6f..075fbd94d42 100644
--- a/core/src/clients/telegram/src/messageManager.ts
+++ b/core/src/clients/telegram/src/messageManager.ts
@@ -89,6 +89,7 @@ export class MessageManager {
         state: State
     ): Promise<boolean> {
         // Respond if bot is mentioned
+
         if (
             "text" in message &&
             message.text?.includes(`@${this.bot.botInfo?.username}`)
diff --git a/core/src/index.ts b/core/src/index.ts
index 06f56838998..74804d5bb2e 100644
--- a/core/src/index.ts
+++ b/core/src/index.ts
@@ -4,264 +4,45 @@ export * from "./clients/index.ts";
 export * from "./adapters/index.ts";
 export * from "./providers/index.ts";
 
-import * as Action from "./actions/index.ts";
 import * as Client from "./clients/index.ts";
-import * as Adapter from "./adapters/index.ts";
-import * as Provider from "./providers/index.ts";
 
-import Database from "better-sqlite3";
-import fs from "fs";
-import yargs from "yargs";
-
-import { wait } from "./clients/twitter/utils.ts";
-import { defaultActions } from "./core/actions.ts";
-import defaultCharacter from "./core/defaultCharacter.ts";
-import { AgentRuntime } from "./core/runtime.ts";
-import settings from "./core/settings.ts";
-import { Character, IAgentRuntime, ModelProvider } from "./core/types.ts"; // Added IAgentRuntime
+import { Character } from "./core/types.ts";
 
 import readline from "readline";
+import { Arguments } from "./types/index.ts";
+import {
+    createAgentRuntime,
+    createDirectRuntime,
+    getTokenForProvider,
+    initializeClients,
+    initializeDatabase,
+    loadCharacters,
+    parseArguments,
+} from "./cli/index.ts";
 
-interface Arguments {
-    character?: string;
-    characters?: string;
-    //twitter?: boolean;
-    discord?: boolean;
-    telegram?: boolean; // Added telegram option
-}
-
-let argv: Arguments = {
-    character: "./src/agent/default_character.json",
-    characters: "",
-};
-
-try {
-    // Parse command line arguments
-    argv = yargs(process.argv.slice(2))
-        .option("character", {
-            type: "string",
-            description: "Path to the character JSON file",
-        })
-        .option("characters", {
-            type: "string",
-            description:
-                "Comma separated list of paths to character JSON files",
-        })
-        .option("telegram", {
-            type: "boolean",
-            description: "Enable Telegram client",
-            default: false,
-        })
-        .parseSync() as Arguments;
-} catch (error) {
-    console.log("Error parsing arguments:");
-    console.log(error);
-}
-
-// Load character
-const characterPath = argv.character || argv.characters;
+let argv: Arguments = parseArguments();
 
-const characterPaths = argv.characters?.split(",").map((path) => path.trim());
-
-const characters = [];
+const characters = loadCharacters(argv.characters);
 
 const directClient = new Client.DirectClient();
-directClient.start(3000);
-
-if (characterPaths?.length > 0) {
-    for (const path of characterPaths) {
-        try {
-            const character = JSON.parse(fs.readFileSync(path, "utf8"));
-            characters.push(character);
-        } catch (e) {
-            console.log(`Error loading character from ${path}: ${e}`);
-        }
-    }
-}
 
-function getTokenForProvider(provider: ModelProvider, character: Character) {
-    switch (provider) {
-        case ModelProvider.OPENAI:
-            return (
-                character.settings?.secrets?.OPENAI_API_KEY ||
-                (settings.OPENAI_API_KEY as string)
-            );
-        case ModelProvider.ANTHROPIC:
-            return (
-                character.settings?.secrets?.CLAUDE_API_KEY ||
-                (settings.CLAUDE_API_KEY as string)
-            );
-    }
-}
+directClient.start(3000);
 
 async function startAgent(character: Character) {
-    console.log("Starting agent for character " + character.name);
+    console.log(`Starting agent for character ${character.name}`);
     const token = getTokenForProvider(character.modelProvider, character);
+    const db = initializeDatabase();
 
-    let db;
-    if (process.env.POSTGRES_URL) {
-        // const db = new SqliteDatabaseAdapter(new Database("./db.sqlite"));
-        db = new Adapter.PostgresDatabaseAdapter({
-            connectionString: process.env.POSTGRES_URL,
-        });
-    } else {
-        db = new Adapter.SqliteDatabaseAdapter(new Database("./db.sqlite"));
-        // Debug adapter
-        // const loggingDb = createLoggingDatabaseAdapter(db);
-    }
-
-    const runtime = new AgentRuntime({
-        databaseAdapter: db,
-        token,
-        modelProvider: character.modelProvider,
-        evaluators: [],
-        character,
-        providers: [
-            Provider.timeProvider,
-            Provider.boredomProvider,
-            Provider.walletProvider,
-        ],
-        actions: [
-            ...defaultActions,
-            Action.askClaude,
-            Action.followRoom,
-            Action.unfollowRoom,
-            Action.unmuteRoom,
-            Action.muteRoom,
-            Action.imageGeneration,
-            Action.executeSwap,
-        ],
-    });
-
-    const directRuntime = new AgentRuntime({
-        databaseAdapter: db,
-        token,
-        modelProvider: character.modelProvider,
-        evaluators: [],
-        character,
-        providers: [
-            Provider.timeProvider,
-            Provider.boredomProvider,
-            Provider.walletProvider,
-            Provider.orderBookProvider,
-            Provider.tokenProvider,
-        ],
-        actions: [...defaultActions],
-    });
-
-    function startDiscord(runtime: IAgentRuntime) {
-        const discordClient = new Client.DiscordClient(runtime);
-        return discordClient;
-    }
-
-    async function startTelegram(runtime: IAgentRuntime, character: Character) {
-        console.log("🔍 Attempting to start Telegram bot...");
-
-        const botToken = runtime.getSetting("TELEGRAM_BOT_TOKEN");
-
-        if (!botToken) {
-            console.error(
-                `❌ Telegram bot token is not set for character ${character.name}.`
-            );
-            return null;
-        }
-
-        console.log("✅ Bot token found, initializing Telegram client...");
-
-        try {
-            console.log("Creating new TelegramClient instance...");
-            const telegramClient = new Client.TelegramClient(runtime, botToken);
-
-            console.log("Calling start() on TelegramClient...");
-            await telegramClient.start();
-
-            console.log(
-                `✅ Telegram client successfully started for character ${character.name}`
-            );
-            return telegramClient;
-        } catch (error) {
-            console.error(
-                `❌ Error creating/starting Telegram client for ${character.name}:`,
-                error
-            );
-            return null;
-        }
-    }
-
-    async function startTwitter(runtime) {
-        console.log("Starting search client");
-        const twitterSearchClient = new Client.TwitterSearchClient(runtime);
-        await wait();
-        console.log("Starting interaction client");
-        const twitterInteractionClient = new Client.TwitterInteractionClient(
-            runtime
-        );
-        await wait();
-        console.log("Starting generation client");
-        const twitterGenerationClient = new Client.TwitterGenerationClient(
-            runtime
-        );
-
-        return {
-            twitterInteractionClient,
-            twitterSearchClient,
-            twitterGenerationClient,
-        };
-    }
-
-    if (!character.clients) {
-        return console.error(
-            "No clients found for character " + character.name
-        );
-    }
-
-    const clients = [];
-
-    if (character.clients.map((str) => str.toLowerCase()).includes("discord")) {
-        const discordClient = startDiscord(runtime);
-        clients.push(discordClient);
-    }
-
-    // Add Telegram client initialization
-    if (
-        argv.telegram ||
-        character.clients.map((str) => str.toLowerCase()).includes("telegram")
-    ) {
-        console.log("🔄 Telegram client enabled, starting initialization...");
-        const telegramClient = await startTelegram(runtime, character);
-        if (telegramClient) {
-            console.log(
-                "✅ Successfully added Telegram client to active clients"
-            );
-            clients.push(telegramClient);
-        } else {
-            console.log("❌ Failed to initialize Telegram client");
-        }
-    }
-
-    if (character.clients.map((str) => str.toLowerCase()).includes("twitter")) {
-        const {
-            twitterInteractionClient,
-            twitterSearchClient,
-            twitterGenerationClient,
-        } = await startTwitter(runtime);
-        clients.push(
-            twitterInteractionClient,
-            twitterSearchClient,
-            twitterGenerationClient
-        );
-    }
+    const runtime = createAgentRuntime(character, db, token);
+    const directRuntime = createDirectRuntime(character, db, token);
 
+    const clients = await initializeClients(character, runtime);
     directClient.registerAgent(directRuntime);
 
     return clients;
 }
 
 const startAgents = async () => {
-    if (characters.length === 0) {
-        console.log("No characters found, using default character");
-        characters.push(defaultCharacter);
-    }
     for (const character of characters) {
         await startAgent(character);
     }
@@ -281,7 +62,7 @@ function chat() {
             return;
         }
 
-        const agentId = characters[0].name.toLowerCase(); // Assuming we're using the first character
+        const agentId = characters[0].name.toLowerCase();
         const response = await fetch(
             `http://localhost:3000/${agentId}/message`,
             {
diff --git a/core/src/types/index.ts b/core/src/types/index.ts
new file mode 100644
index 00000000000..2c5cbb1ab12
--- /dev/null
+++ b/core/src/types/index.ts
@@ -0,0 +1,8 @@
+interface Arguments {
+    character?: string;
+    characters?: string;
+    discord?: boolean;
+    telegram?: boolean;
+}
+
+export type { Arguments };

From dce8bae54d0e560eeac21f81aaf079c66d561d9f Mon Sep 17 00:00:00 2001
From: ponderingdemocritus <ponderingdemocritus@protonmail.com>
Date: Sat, 2 Nov 2024 15:06:43 +1100
Subject: [PATCH 2/2] allow custom actions

---
 README.md                     | 107 ++++++++++++++++++++--------------
 core/.gitignore               |   2 +
 core/elizaConfig.example.yaml |  11 ++++
 core/package.json             |   2 +
 core/src/cli/config.ts        |  45 ++++++++++++++
 core/src/cli/index.ts         |  16 ++++-
 core/src/index.ts             |   2 +-
 pnpm-lock.yaml                |  12 ++++
 8 files changed, 149 insertions(+), 48 deletions(-)
 create mode 100644 core/elizaConfig.example.yaml
 create mode 100644 core/src/cli/config.ts

diff --git a/README.md b/README.md
index 9a6bc01a6b7..0fffaea20af 100644
--- a/README.md
+++ b/README.md
@@ -17,24 +17,35 @@ _As seen powering [@DegenSpartanAI](https://x.com/degenspartanai) and [@MarcAInd
 
 # Getting Started
 
-## Install Node.js
+**Prerequisites (MUST):**
 
-https://docs.npmjs.com/downloading-and-installing-node-js-and-npm
+-   [Node.js 22+](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)
+-   [pnpm](https://pnpm.io/installation)
 
-## Using pnpm
-
-We use pnpm to manage our dependencies. It is faster and more efficient than npm, and it supports workspaces.
-https://pnpm.io/installation
-
-## Edit the .env file
+### Edit the .env file
 
 -   Copy .env.example to .env and fill in the appropriate values
 -   Edit the TWITTER environment variables to add your bot's username and password
 
-## Edit the character file
+### Edit the character file
 
 -   Check out the file `src/core/defaultCharacter.ts` - you can modify this
--   You can also load characters with the `node --loader ts-node/esm src/index.ts --characters="path/to/your/character.json"` and run multiple bots at the same time.
+-   You can also load characters with the `pnpm start --characters="path/to/your/character.json"` and run multiple bots at the same time.
+
+After setting up the .env file and character file, you can start the bot with the following command:
+
+```
+pnpm i
+pnpm start
+```
+
+# Customising Eliza
+
+### Adding custom actions
+
+To avoid git clashes in the core directory, we recommend adding custom actions to a `custom_actions` directory and then adding them to the `elizaConfig.yaml` file. See the `elizaConfig.example.yaml` file for an example.
+
+## Running with different models
 
 ### Run with Llama
 
@@ -48,10 +59,6 @@ You can run Grok models by setting the `XAI_MODEL` environment variable to `grok
 
 You can run OpenAI models by setting the `XAI_MODEL` environment variable to `gpt-4o-mini` or `gpt-4o`
 
-# Requires Node 20+
-
-If you are getting strange issues when starting up, make sure you're using Node 20+. Some APIs are not compatible with previous versions. You can check your node version with `node -v`. If you need to install a new version of node, we recommend using [nvm](https://github.com/nvm-sh/nvm).
-
 ## Additional Requirements
 
 You may need to install Sharp. If you see an error when starting up, try installing it with the following command:
@@ -66,20 +73,55 @@ You will need to add environment variables to your .env file to connect to vario
 
 ```
 # Required environment variables
-# Start Discord
 DISCORD_APPLICATION_ID=
 DISCORD_API_TOKEN= # Bot token
+OPENAI_API_KEY=sk-* # OpenAI API key, starting with sk-
+ELEVENLABS_XI_API_KEY= # API key from elevenlabs
 
-# Start Twitter
+# ELEVENLABS SETTINGS
+ELEVENLABS_MODEL_ID=eleven_multilingual_v2
+ELEVENLABS_VOICE_ID=21m00Tcm4TlvDq8ikWAM
+ELEVENLABS_VOICE_STABILITY=0.5
+ELEVENLABS_VOICE_SIMILARITY_BOOST=0.9
+ELEVENLABS_VOICE_STYLE=0.66
+ELEVENLABS_VOICE_USE_SPEAKER_BOOST=false
+ELEVENLABS_OPTIMIZE_STREAMING_LATENCY=4
+ELEVENLABS_OUTPUT_FORMAT=pcm_16000
+
+TWITTER_DRY_RUN=false
 TWITTER_USERNAME= # Account username
 TWITTER_PASSWORD= # Account password
 TWITTER_EMAIL= # Account email
 TWITTER_COOKIES= # Account cookies
+
+X_SERVER_URL=
+XAI_API_KEY=
+XAI_MODEL=
+
+
+# For asking Claude stuff
+ANTHROPIC_API_KEY=
+
+WALLET_SECRET_KEY=EXAMPLE_WALLET_SECRET_KEY
+WALLET_PUBLIC_KEY=EXAMPLE_WALLET_PUBLIC_KEY
+
+BIRDEYE_API_KEY=
+
+SOL_ADDRESS=So11111111111111111111111111111111111111112
+SLIPPAGE=1
+RPC_URL=https://api.mainnet-beta.solana.com
+HELIUS_API_KEY=
+
+
+## Telegram
+TELEGRAM_BOT_TOKEN=
+
+TOGETHER_API_KEY=
 ```
 
-# Local Setup
+# Local Inference Setup
 
-## CUDA Setup
+### CUDA Setup
 
 If you have an NVIDIA GPU, you can install CUDA to speed up local inference dramatically.
 
@@ -90,37 +132,14 @@ npx --no node-llama-cpp source download --gpu cuda
 
 Make sure that you've installed the CUDA Toolkit, including cuDNN and cuBLAS.
 
-## Running locally
+### Running locally
 
 Add XAI_MODEL and set it to one of the above options from [Run with
 Llama](#run-with-llama) - you can leave X_SERVER_URL and XAI_API_KEY blank, it
 downloads the model from huggingface and queries it locally
 
-# Cloud Setup (with OpenAI)
-
-In addition to the environment variables above, you will need to add the following:
-
-```
-# OpenAI handles the bulk of the work with chat, TTS, image recognition, etc.
-OPENAI_API_KEY=sk-* # OpenAI API key, starting with sk-
-
-# The agent can also ask Claude for help if you have an API key
-ANTHROPIC_API_KEY=
-
-# For Elevenlabs voice generation on Discord voice
-ELEVENLABS_XI_API_KEY= # API key from elevenlabs
-
-# ELEVENLABS SETTINGS
-ELEVENLABS_MODEL_ID=eleven_multilingual_v2
-ELEVENLABS_VOICE_ID=21m00Tcm4TlvDq8ikWAM
-ELEVENLABS_VOICE_STABILITY=0.5
-ELEVENLABS_VOICE_SIMILARITY_BOOST=0.9
-ELEVENLABS_VOICE_STYLE=0.66
-ELEVENLABS_VOICE_USE_SPEAKER_BOOST=false
-ELEVENLABS_OPTIMIZE_STREAMING_LATENCY=4
-ELEVENLABS_OUTPUT_FORMAT=pcm_16000
-```
+# Clients
 
-# Discord Bot
+## Discord Bot
 
 For help with setting up your Discord Bot, check out here: https://discordjs.guide/preparations/setting-up-a-bot-application.html
diff --git a/core/.gitignore b/core/.gitignore
index f06235c460c..e1a400663fa 100644
--- a/core/.gitignore
+++ b/core/.gitignore
@@ -1,2 +1,4 @@
 node_modules
 dist
+elizaConfig.yaml
+custom_actions/
\ No newline at end of file
diff --git a/core/elizaConfig.example.yaml b/core/elizaConfig.example.yaml
new file mode 100644
index 00000000000..c271f7b269f
--- /dev/null
+++ b/core/elizaConfig.example.yaml
@@ -0,0 +1,11 @@
+# Load custom actions from the actions directory
+
+# Close this into a elizaConfig.yaml file that is ignored by git
+
+# Paths are relative to the core/src directory
+
+actions:
+    - name: askClaude
+      path: ./actions/askClaude.ts
+    - name: epicAction
+      path: ./custom_actions/epicAction.ts
diff --git a/core/package.json b/core/package.json
index 5d9416f0af9..dd4ace00a04 100644
--- a/core/package.json
+++ b/core/package.json
@@ -124,8 +124,10 @@
         "html-escaper": "3.0.3",
         "html-to-text": "9.0.5",
         "import-meta-resolve": "4.1.0",
+        "install": "^0.13.0",
         "jieba-wasm": "2.2.0",
         "js-sha1": "0.7.0",
+        "js-yaml": "^4.1.0",
         "json5": "2.2.3",
         "kuromoji": "0.1.2",
         "libsodium-wrappers": "0.7.15",
diff --git a/core/src/cli/config.ts b/core/src/cli/config.ts
new file mode 100644
index 00000000000..05b7bda964e
--- /dev/null
+++ b/core/src/cli/config.ts
@@ -0,0 +1,45 @@
+import fs from "fs";
+import yaml from "js-yaml";
+import path from "path";
+import { fileURLToPath } from "url";
+import { Action } from "../core/types";
+
+const ROOT_DIR = path.resolve(fileURLToPath(import.meta.url), "../../../src");
+
+interface ActionConfig {
+    name: string;
+    path: string;
+}
+
+export function loadActionConfigs(configPath: string): ActionConfig[] {
+    if (!fs.existsSync(configPath)) {
+        console.error(`Config file not found at path: ${configPath}`);
+        return [];
+    }
+
+    const configFile = fs.readFileSync(configPath, "utf8");
+    const parsedConfig = yaml.load(configFile) as { actions: ActionConfig[] };
+    return parsedConfig?.actions || [];
+}
+
+export async function loadCustomActions(
+    actionConfigs: ActionConfig[]
+): Promise<Action[]> {
+    const actions = [];
+
+    for (const config of actionConfigs) {
+        const resolvedPath = path.resolve(ROOT_DIR, config.path);
+        console.log(`Importing action from: ${resolvedPath}`); // Debugging log
+
+        try {
+            const actionModule = await import(resolvedPath);
+            actions.push(actionModule[config.name]);
+        } catch (error) {
+            console.error(
+                `Failed to import action from ${resolvedPath}:`,
+                error
+            );
+        }
+    }
+    return actions;
+}
diff --git a/core/src/cli/index.ts b/core/src/cli/index.ts
index d6f21138b06..ad14b208c6a 100644
--- a/core/src/cli/index.ts
+++ b/core/src/cli/index.ts
@@ -13,6 +13,7 @@ import Database from "better-sqlite3";
 import { AgentRuntime } from "../core/runtime.ts";
 import { defaultActions } from "../core/actions.ts";
 import { Arguments } from "../types/index.ts";
+import { loadActionConfigs, loadCustomActions } from "./config.ts";
 
 export async function initializeClients(
     character: Character,
@@ -113,11 +114,15 @@ export function initializeDatabase() {
     }
 }
 
-export function createAgentRuntime(
+export async function createAgentRuntime(
     character: Character,
     db: any,
-    token: string
+    token: string,
+    configPath: string = "./elizaConfig.yaml"
 ) {
+    const actionConfigs = loadActionConfigs(configPath);
+    const customActions = await loadCustomActions(actionConfigs);
+
     return new AgentRuntime({
         databaseAdapter: db,
         token,
@@ -126,13 +131,18 @@ export function createAgentRuntime(
         character,
         providers: [Provider.timeProvider, Provider.boredomProvider],
         actions: [
+            // Default actions
             ...defaultActions,
-            Action.askClaude,
+
+            // Custom actions
             Action.followRoom,
             Action.unfollowRoom,
             Action.unmuteRoom,
             Action.muteRoom,
             Action.imageGeneration,
+
+            // imported from elizaConfig.yaml
+            ...customActions,
         ],
     });
 }
diff --git a/core/src/index.ts b/core/src/index.ts
index 74804d5bb2e..b017d0be27e 100644
--- a/core/src/index.ts
+++ b/core/src/index.ts
@@ -33,7 +33,7 @@ async function startAgent(character: Character) {
     const token = getTokenForProvider(character.modelProvider, character);
     const db = initializeDatabase();
 
-    const runtime = createAgentRuntime(character, db, token);
+    const runtime = await createAgentRuntime(character, db, token);
     const directRuntime = createDirectRuntime(character, db, token);
 
     const clients = await initializeClients(character, runtime);
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 2b44084f931..6d7fefc40a8 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -188,12 +188,18 @@ importers:
       import-meta-resolve:
         specifier: 4.1.0
         version: 4.1.0
+      install:
+        specifier: ^0.13.0
+        version: 0.13.0
       jieba-wasm:
         specifier: 2.2.0
         version: 2.2.0
       js-sha1:
         specifier: 0.7.0
         version: 0.7.0
+      js-yaml:
+        specifier: ^4.1.0
+        version: 4.1.0
       json5:
         specifier: 2.2.3
         version: 2.2.3
@@ -6227,6 +6233,10 @@ packages:
     resolution: {integrity: sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==}
     engines: {node: '>=12.0.0'}
 
+  install@0.13.0:
+    resolution: {integrity: sha512-zDml/jzr2PKU9I8J/xyZBQn8rPCAY//UOYNmR01XwNwyfhEWObo2SWfSl1+0tm1u6PhxLwDnfsT/6jB7OUxqFA==}
+    engines: {node: '>= 0.10'}
+
   interpret@1.4.0:
     resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==}
     engines: {node: '>= 0.10'}
@@ -18193,6 +18203,8 @@ snapshots:
       through: 2.3.8
       wrap-ansi: 6.2.0
 
+  install@0.13.0: {}
+
   interpret@1.4.0: {}
 
   invariant@2.2.4: