Files
ajarbot/examples/example_bot_with_skills.py

175 lines
4.8 KiB
Python
Raw Normal View History

"""
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())