""" Example: Bot runner with local skills support. This demonstrates how to use local skills from .claude/skills/ in your bot, allowing users to invoke them from Slack/Telegram. """ import asyncio from adapters.base import AdapterConfig, InboundMessage from adapters.runtime import AdapterRuntime from adapters.skill_integration import SkillInvoker from adapters.slack.adapter import SlackAdapter from adapters.telegram.adapter import TelegramAdapter from agent import Agent def create_skill_preprocessor( skill_invoker: SkillInvoker, agent: Agent ) -> callable: """ Preprocessor that allows invoking skills via /skill-name syntax. Example messages: "/adapter-dev create Discord adapter" "/help" "/status check health of all adapters" """ def preprocessor( message: InboundMessage, ) -> InboundMessage: text = message.text.strip() if not text.startswith("/"): return message parts = text.split(maxsplit=1) skill_name = parts[0][1:] args = parts[1] if len(parts) > 1 else "" available_skills = skill_invoker.list_available_skills() if skill_name in available_skills: print( f"[Skills] Invoking /{skill_name} " f"with args: {args}" ) skill_info = skill_invoker.get_skill_info(skill_name) if skill_info: skill_body = skill_info.get("body", "") skill_context = skill_body.replace( "$ARGUMENTS", args, ) arg_parts = args.split() if args else [] for i, arg in enumerate(arg_parts): skill_context = skill_context.replace( f"${i}", arg, ) message.text = skill_context message.metadata["skill_invoked"] = skill_name print(f"[Skills] Skill /{skill_name} loaded") else: message.text = ( f"Error: Could not load skill '{skill_name}'" ) elif skill_name in ["help", "skills"]: skills_list = "\n".join( f" - /{s}" for s in available_skills ) message.text = ( f"Available skills:\n{skills_list}\n\n" f"Use /skill-name to invoke." ) message.metadata["builtin_command"] = True return message return preprocessor def create_skill_postprocessor() -> callable: """Postprocessor that adds skill metadata to responses.""" def postprocessor( response: str, original: InboundMessage, ) -> str: if original.metadata.get("skill_invoked"): skill_name = original.metadata["skill_invoked"] response += f"\n\n_[Powered by /{skill_name}]_" return response return postprocessor async def main() -> None: print("=" * 60) print("Ajarbot with Local Skills") print("=" * 60) # 1. Create agent agent = Agent( provider="claude", workspace_dir="./memory_workspace", enable_heartbeat=False, ) # 2. Initialize skill system skill_invoker = SkillInvoker() print("\n[Skills] Available local skills:") for skill in skill_invoker.list_available_skills(): info = skill_invoker.get_skill_info(skill) desc = ( info.get("description", "No description") if info else "Unknown" ) print(f" - /{skill} - {desc}") # 3. Create runtime with skill support runtime = AdapterRuntime(agent) runtime.add_preprocessor( create_skill_preprocessor(skill_invoker, agent), ) runtime.add_postprocessor(create_skill_postprocessor()) # 4. Add adapters slack_adapter = SlackAdapter(AdapterConfig( platform="slack", enabled=True, credentials={ "bot_token": "xoxb-YOUR-TOKEN", "app_token": "xapp-YOUR-TOKEN", }, )) runtime.add_adapter(slack_adapter) telegram_adapter = TelegramAdapter(AdapterConfig( platform="telegram", enabled=True, credentials={"bot_token": "YOUR-TELEGRAM-TOKEN"}, )) runtime.add_adapter(telegram_adapter) print("\n[Setup] Bot configured with skill support") print("\nUsers can now invoke skills from Slack/Telegram:") print(" Example: '/adapter-dev create Discord adapter'") print(" Example: '/skills' (list available skills)") # 5. Start runtime await runtime.start() print("\n" + "=" * 60) print("Bot is running! Press Ctrl+C to stop.") print("=" * 60 + "\n") try: while True: await asyncio.sleep(1) except KeyboardInterrupt: print("\n[Shutdown] Stopping...") finally: await runtime.stop() if __name__ == "__main__": asyncio.run(main())