CREATING_EXTENSIONS.md
1 # Creating Kamaji Extensions: Commands, Agents, and Tools 2 3 Complete guide to extending Kamaji with custom commands, specialized agents, and powerful tools. 4 5 --- 6 7 ## Table of Contents 8 9 1. [Architecture Overview](#architecture-overview) 10 2. [Creating Commands](#creating-commands) 11 3. [Creating Agents](#creating-agents) 12 4. [Creating Tools](#creating-tools) 13 5. [Examples](#examples) 14 15 --- 16 17 ## Architecture Overview 18 19 ### The Three Layers 20 21 ``` 22 ┌─────────────────────────────────────────────────────┐ 23 │ COMMANDS │ 24 │ Entry points (CLI, TUI) that users interact with │ 25 │ Examples: kamaji chat, kamaji work, /add │ 26 └────────────────┬────────────────────────────────────┘ 27 │ 28 ▼ 29 ┌─────────────────────────────────────────────────────┐ 30 │ AGENTS │ 31 │ Intelligent decision-makers using LLMs │ 32 │ Examples: @review, @docs, general agent │ 33 └────────────────┬────────────────────────────────────┘ 34 │ 35 ▼ 36 ┌─────────────────────────────────────────────────────┐ 37 │ TOOLS │ 38 │ Atomic functions agents can execute │ 39 │ Examples: read_file, execute_shell_command │ 40 └─────────────────────────────────────────────────────┘ 41 ``` 42 43 ### Key Differences 44 45 | Type | Purpose | Has LLM? | User-Facing? | Location | 46 |------|---------|----------|--------------|----------| 47 | **Command** | Entry point | No | Yes | `kamaji/commands/` | 48 | **Agent** | Decision logic | Yes | Via @ or auto-route | `kamaji/agents/` | 49 | **Tool** | Atomic action | No | No (agent-only) | `kamaji/tools.py` | 50 51 --- 52 53 ## Creating Commands 54 55 ### Command Structure 56 57 Commands are entry points that users invoke directly. They can be: 58 - **CLI commands**: `kamaji <command>` (e.g., `kamaji chat`) 59 - **TUI commands**: Special syntax in TUI (e.g., `explain`, `memory`) 60 61 ### File Location 62 63 ``` 64 kamaji/commands/your_command.py 65 ``` 66 67 ### Template: CLI Command 68 69 ```python 70 """ 71 Your command description. 72 """ 73 74 import sys 75 from pathlib import Path 76 77 78 def run_your_command(arg1: str = None, arg2: bool = False): 79 """ 80 Main entry point for your command. 81 82 Args: 83 arg1: Description of argument 1 84 arg2: Description of argument 2 85 86 Returns: 87 Exit code (0 for success, 1 for error) 88 """ 89 try: 90 print("🚀 Your Command Running...") 91 92 # Your command logic here 93 result = do_something(arg1) 94 95 print(f"✅ Success: {result}") 96 return 0 97 98 except KeyboardInterrupt: 99 print("\n\n👋 Interrupted by user\n") 100 return 0 101 except Exception as e: 102 print(f"\n❌ Error: {e}\n", file=sys.stderr) 103 return 1 104 105 106 def do_something(input_value): 107 """Helper function for your command logic.""" 108 # Implementation here 109 return f"Processed: {input_value}" 110 ``` 111 112 ### Template: TUI Command Handler 113 114 ```python 115 # In kamaji/commands/tui.py 116 117 def on_input_submitted(self, event: Input.Submitted) -> None: 118 """Handle when user submits a message.""" 119 # ... existing code ... 120 121 # Add your command handler 122 if user_input.lower().startswith("yourcommand "): 123 self.handle_your_command(user_input[12:].strip()) 124 return 125 126 # ... rest of code ... 127 128 def handle_your_command(self, args: str) -> None: 129 """Handle the 'yourcommand' command.""" 130 from rich.table import Table 131 132 messages = self.query_one("#messages", MessageDisplay) 133 134 # Show user command 135 messages.write("") 136 messages.write(Panel( 137 f"[#4facfe]⚡ yourcommand {args}[/#4facfe]", 138 title="[bold #667eea]👤 You[/bold #667eea]", 139 border_style="round", 140 title_align="left" 141 )) 142 143 # Your command logic 144 try: 145 result = process_command(args) 146 147 # Display result 148 messages.write(Panel( 149 result, 150 title="[bold #27ae60]✅ Success[/bold #27ae60]", 151 border_style="bold #27ae60", 152 title_align="center" 153 )) 154 messages.call_after_refresh(messages.scroll_end) 155 156 except Exception as e: 157 messages.write(Panel( 158 f"❌ Error: {str(e)}", 159 title="[bold #ff5252]⚠️ Error[/bold #ff5252]", 160 border_style="round" 161 )) 162 messages.call_after_refresh(messages.scroll_end) 163 ``` 164 165 ### Registration 166 167 #### 1. Export from `kamaji/commands/__init__.py`: 168 169 ```python 170 from kamaji.commands.your_command import run_your_command 171 172 __all__ = [ 173 # ... existing exports ... 174 'run_your_command' 175 ] 176 ``` 177 178 #### 2. Register in `kamaji/cli.py`: 179 180 ```python 181 from kamaji.commands import run_your_command # Add import 182 183 # In main(): 184 subparsers = parser.add_subparsers(dest='command', help='Command to run') 185 186 # Add your command parser 187 your_parser = subparsers.add_parser('yourcommand', help='Description of your command') 188 your_parser.add_argument('arg1', help='First argument') 189 your_parser.add_argument('--arg2', action='store_true', help='Optional flag') 190 191 # In routing section: 192 if args.command == 'yourcommand': 193 exit_code = run_your_command(args.arg1, args.arg2) 194 sys.exit(exit_code) 195 ``` 196 197 #### 3. Add to TUI autocomplete in `kamaji/autocomplete.py`: 198 199 ```python 200 self.commands: Dict[str, Tuple[str, str]] = { 201 # ... existing commands ... 202 "yourcommand": ("⚡ Description of your command", "category"), 203 } 204 ``` 205 206 --- 207 208 ## Creating Agents 209 210 ### Agent Structure 211 212 Agents are specialized AI assistants that use LLMs to make decisions and execute tools. They follow the ReAct pattern (Reasoning + Acting). 213 214 ### File Location 215 216 ``` 217 kamaji/agents/your_agent.py 218 ``` 219 220 ### Template: Specialized Agent 221 222 ```python 223 """ 224 Your specialized agent description. 225 """ 226 227 from langchain.agents import Tool, AgentExecutor, create_react_agent 228 from langchain.prompts import PromptTemplate 229 from langchain_core.language_models import BaseLLM 230 231 from kamaji.tools import read_file, write_file_smart, execute_shell_command 232 233 234 def custom_tool_function(input_text: str) -> str: 235 """ 236 A custom tool specific to this agent. 237 238 Args: 239 input_text: Input for the tool 240 241 Returns: 242 Result string 243 """ 244 try: 245 # Your tool implementation 246 result = process_input(input_text) 247 return f"✅ Result: {result}" 248 except Exception as e: 249 return f"❌ Error: {str(e)}" 250 251 252 def create_your_agent(llm: BaseLLM, verbose: bool = False) -> AgentExecutor: 253 """ 254 Create your specialized agent. 255 256 This agent is optimized for [specific task description]. 257 258 Args: 259 llm: The language model to use 260 verbose: Show detailed reasoning 261 262 Returns: 263 AgentExecutor configured for your task 264 """ 265 # Define tools this agent can use 266 tools = [ 267 Tool( 268 name="read_file", 269 func=read_file, 270 description="Read contents of a file. Input should be the file path." 271 ), 272 Tool( 273 name="write_file", 274 func=write_file_smart, 275 description="Write content to a file. Format: 'filepath|||content'" 276 ), 277 Tool( 278 name="custom_tool", 279 func=custom_tool_function, 280 description="Description of what your custom tool does. Input: what it expects." 281 ), 282 Tool( 283 name="shell", 284 func=execute_shell_command, 285 description="Execute shell commands. Input should be a shell command." 286 ), 287 ] 288 289 # Define the agent's personality and instructions 290 prompt = PromptTemplate.from_template(""" 291 You are a specialized AI assistant for [your domain]. 292 293 Your responsibilities: 294 - [Responsibility 1] 295 - [Responsibility 2] 296 - [Responsibility 3] 297 298 Guidelines: 299 ✅ [Guideline 1] 300 ✅ [Guideline 2] 301 ✅ [Guideline 3] 302 303 Available tools: {tools} 304 305 Use the following format: 306 307 Question: {input} 308 Thought: I should analyze what needs to be done 309 Action: [one of: {tool_names}] 310 Action Input: [input for the tool] 311 Observation: [tool result] 312 ... (repeat Thought/Action/Observation as needed) 313 Thought: I have completed the task 314 Final Answer: [your comprehensive response] 315 316 Begin! 317 318 Question: {input} 319 Thought:{agent_scratchpad} 320 """) 321 322 # Create the ReAct agent 323 agent = create_react_agent(llm, tools, prompt) 324 325 # Return configured executor 326 return AgentExecutor( 327 agent=agent, 328 tools=tools, 329 verbose=verbose, 330 handle_parsing_errors=True, 331 max_iterations=30, # Adjust based on task complexity 332 early_stopping_method="generate" 333 ) 334 ``` 335 336 ### Agent Registration 337 338 #### 1. Export from `kamaji/agents/__init__.py`: 339 340 ```python 341 from kamaji.agents.your_agent import create_your_agent 342 343 __all__ = [ 344 # ... existing exports ... 345 'create_your_agent' 346 ] 347 ``` 348 349 #### 2. Add to AgentRouter in `kamaji/agents/router.py`: 350 351 ```python 352 class AgentRouter: 353 def __init__(self, llm: BaseLLM, verbose: bool = False): 354 self.llm = llm 355 self.verbose = verbose 356 357 self._agents: Dict[str, Optional[AgentExecutor]] = { 358 "general": None, 359 "review": None, 360 "docs": None, 361 "youragent": None, # Add your agent 362 } 363 364 self._keywords = { 365 "review": ["review", "check code", "analyze code"], 366 "docs": ["document", "documentation", "readme"], 367 "youragent": ["keyword1", "keyword2", "keyword3"], # Add keywords 368 } 369 370 def _get_agent(self, agent_name: str, **kwargs) -> AgentExecutor: 371 # ... existing code ... 372 373 elif agent_name == "youragent": 374 from kamaji.agents.your_agent import create_your_agent 375 self._agents["youragent"] = create_your_agent(self.llm, verbose=verbose) 376 377 # ... rest of code ... 378 379 def help(self) -> str: 380 """Get help text showing available agents.""" 381 return """ 382 🔥 Available Specialized Agents: 383 384 @review - Code review specialist 385 • Reviews code for bugs, security, style 386 • Checks test coverage 387 • Keywords: review, check code, analyze 388 389 @docs - Documentation specialist 390 • Writes/improves documentation 391 • Creates README files 392 • Keywords: document, readme, docstring 393 394 @youragent - Your agent description 395 • [What it does 1] 396 • [What it does 2] 397 • Keywords: keyword1, keyword2 398 399 Usage: 400 • @agent_name <task> - Use specific agent 401 • Or just type your request - auto-routes to best agent 402 """ 403 ``` 404 405 #### 3. Add to TUI autocomplete: 406 407 ```python 408 # In kamaji/autocomplete.py 409 self.commands: Dict[str, Tuple[str, str]] = { 410 # Agent commands 411 "@review": ("Code review specialist agent", "agents"), 412 "@docs": ("Documentation specialist agent", "agents"), 413 "@youragent": ("Your agent description", "agents"), # Add this 414 "@help": ("Show available agents", "agents"), 415 } 416 ``` 417 418 --- 419 420 ## Creating Tools 421 422 ### Tool Structure 423 424 Tools are atomic functions that agents can execute. They should: 425 - Do ONE thing well 426 - Return string results 427 - Handle errors gracefully 428 - Have clear descriptions 429 430 ### File Location 431 432 ``` 433 kamaji/tools.py 434 ``` 435 436 ### Template: Simple Tool 437 438 ```python 439 def your_tool_name(input_param: str) -> str: 440 """ 441 Brief description of what this tool does. 442 443 This tool is used by agents to [purpose]. 444 445 Args: 446 input_param: Description of what input is expected 447 448 Returns: 449 String result or error message 450 451 Example: 452 >>> your_tool_name("example input") 453 "✅ Success: processed example input" 454 """ 455 try: 456 # Validate input 457 if not input_param: 458 return "❌ Error: Input parameter is required" 459 460 # Your tool logic here 461 result = process_something(input_param) 462 463 # Return formatted result 464 return f"✅ Success: {result}" 465 466 except FileNotFoundError as e: 467 return f"❌ File not found: {str(e)}" 468 except PermissionError as e: 469 return f"❌ Permission denied: {str(e)}" 470 except Exception as e: 471 return f"❌ Error: {str(e)}" 472 473 474 def process_something(data): 475 """Helper function for the tool's main logic.""" 476 # Implementation 477 return f"Processed: {data}" 478 ``` 479 480 ### Template: Tool with Multiple Parameters 481 482 Tools typically take a single string input, but you can parse it: 483 484 ```python 485 def multi_param_tool(input_string: str) -> str: 486 """ 487 Tool that accepts multiple parameters. 488 489 Input format: "param1|||param2|||param3" 490 491 Args: 492 input_string: Parameters separated by ||| 493 494 Returns: 495 Result string 496 """ 497 try: 498 # Parse input 499 if "|||" not in input_string: 500 return "❌ Error: Expected format 'param1|||param2|||param3'" 501 502 params = input_string.split("|||") 503 if len(params) != 3: 504 return "❌ Error: Expected 3 parameters" 505 506 param1, param2, param3 = [p.strip() for p in params] 507 508 # Your logic 509 result = do_something(param1, param2, param3) 510 511 return f"✅ Success: {result}" 512 513 except Exception as e: 514 return f"❌ Error: {str(e)}" 515 ``` 516 517 ### Tool Registration 518 519 Tools are registered when creating agents. Add your tool to the tools list: 520 521 ```python 522 from kamaji.tools import your_tool_name 523 524 tools = [ 525 Tool( 526 name="your_tool", 527 func=your_tool_name, 528 description="Clear description of what the tool does and what input it expects." 529 ), 530 # ... other tools ... 531 ] 532 ``` 533 534 --- 535 536 ## Examples 537 538 ### Example 1: Simple CLI Command 539 540 **File**: `kamaji/commands/greet.py` 541 542 ```python 543 """ 544 Greeting command example. 545 """ 546 547 import sys 548 549 550 def run_greet(name: str = "World"): 551 """ 552 Greet the user. 553 554 Args: 555 name: Name to greet 556 557 Returns: 558 Exit code 559 """ 560 try: 561 print(f"\n👋 Hello, {name}!\n") 562 print("Welcome to Kamaji!\n") 563 return 0 564 except Exception as e: 565 print(f"❌ Error: {e}", file=sys.stderr) 566 return 1 567 ``` 568 569 **Register in** `kamaji/cli.py`: 570 571 ```python 572 from kamaji.commands.greet import run_greet 573 574 # Add parser 575 greet_parser = subparsers.add_parser('greet', help='Greet the user') 576 greet_parser.add_argument('--name', default='World', help='Name to greet') 577 578 # Add routing 579 if args.command == 'greet': 580 exit_code = run_greet(args.name) 581 sys.exit(exit_code) 582 ``` 583 584 **Usage**: 585 ```bash 586 kamaji greet 587 kamaji greet --name Alice 588 ``` 589 590 --- 591 592 ### Example 2: Database Agent 593 594 **File**: `kamaji/agents/database.py` 595 596 ```python 597 """ 598 Database specialist agent. 599 """ 600 601 from langchain.agents import Tool, AgentExecutor, create_react_agent 602 from langchain.prompts import PromptTemplate 603 from langchain_core.language_models import BaseLLM 604 605 from kamaji.tools import execute_shell_command, read_file 606 607 608 def query_database(query: str) -> str: 609 """Execute a database query.""" 610 try: 611 import sqlite3 612 # Example implementation 613 conn = sqlite3.connect('database.db') 614 cursor = conn.cursor() 615 cursor.execute(query) 616 results = cursor.fetchall() 617 conn.close() 618 return f"✅ Results: {results}" 619 except Exception as e: 620 return f"❌ Database error: {str(e)}" 621 622 623 def create_database_agent(llm: BaseLLM, verbose: bool = False) -> AgentExecutor: 624 """ 625 Create a database specialist agent. 626 627 This agent helps with database queries, schema design, and optimization. 628 """ 629 tools = [ 630 Tool( 631 name="query_database", 632 func=query_database, 633 description="Execute SQL queries. Input should be a SQL query string." 634 ), 635 Tool( 636 name="read_schema", 637 func=read_file, 638 description="Read database schema file. Input: file path." 639 ), 640 Tool( 641 name="shell", 642 func=execute_shell_command, 643 description="Run database CLI commands. Input: shell command." 644 ), 645 ] 646 647 prompt = PromptTemplate.from_template(""" 648 You are a database specialist. 649 650 Your expertise: 651 - SQL query writing and optimization 652 - Database schema design 653 - Performance tuning 654 - Data analysis 655 656 Available tools: {tools} 657 658 Use the following format: 659 660 Question: {input} 661 Thought: Let me analyze this database task 662 Action: [one of: {tool_names}] 663 Action Input: [input for the tool] 664 Observation: [tool result] 665 ... (repeat as needed) 666 Thought: I have the answer 667 Final Answer: [comprehensive response with SQL queries and explanations] 668 669 Begin! 670 671 Question: {input} 672 Thought:{agent_scratchpad} 673 """) 674 675 agent = create_react_agent(llm, tools, prompt) 676 677 return AgentExecutor( 678 agent=agent, 679 tools=tools, 680 verbose=verbose, 681 handle_parsing_errors=True, 682 max_iterations=20 683 ) 684 ``` 685 686 --- 687 688 ### Example 3: File Statistics Tool 689 690 **File**: `kamaji/tools.py` (add to existing file) 691 692 ```python 693 def get_file_stats(file_path: str) -> str: 694 """ 695 Get detailed statistics about a file. 696 697 Args: 698 file_path: Path to file 699 700 Returns: 701 Formatted statistics string 702 """ 703 try: 704 from pathlib import Path 705 import os 706 707 path = Path(file_path) 708 709 if not path.exists(): 710 return f"❌ File not found: {file_path}" 711 712 # Gather stats 713 size = path.stat().st_size 714 size_kb = size / 1024 715 716 # Count lines for text files 717 try: 718 with open(path, 'r') as f: 719 lines = f.readlines() 720 line_count = len(lines) 721 char_count = sum(len(line) for line in lines) 722 except: 723 line_count = "N/A (binary file)" 724 char_count = "N/A" 725 726 # Format result 727 result = f""" 728 📊 File Statistics: {path.name} 729 730 📏 Size: {size_kb:.2f} KB ({size} bytes) 731 📝 Lines: {line_count} 732 🔤 Characters: {char_count} 733 📂 Directory: {path.parent} 734 🏷️ Extension: {path.suffix} 735 """ 736 return result 737 738 except Exception as e: 739 return f"❌ Error getting file stats: {str(e)}" 740 ``` 741 742 **Usage in an agent**: 743 744 ```python 745 tools = [ 746 Tool( 747 name="file_stats", 748 func=get_file_stats, 749 description="Get detailed statistics about a file. Input: file path." 750 ), 751 ] 752 ``` 753 754 --- 755 756 ## Best Practices 757 758 ### Commands 759 ✅ Keep commands focused on a single responsibility 760 ✅ Provide clear help text and examples 761 ✅ Handle errors gracefully with user-friendly messages 762 ✅ Use consistent emoji/icons for visual feedback 763 ✅ Return proper exit codes (0 = success, 1 = error) 764 765 ### Agents 766 ✅ Write clear, specific prompts for the agent's role 767 ✅ Include only relevant tools (fewer is better) 768 ✅ Provide examples in the prompt if needed 769 ✅ Set appropriate max_iterations (20-30 for complex tasks) 770 ✅ Use handle_parsing_errors=True for robustness 771 772 ### Tools 773 ✅ Do ONE thing well 774 ✅ Return descriptive error messages 775 ✅ Use ✅/❌ prefixes for success/failure 776 ✅ Handle all expected error cases 777 ✅ Keep tool descriptions clear and concise 778 ✅ Document expected input format 779 780 --- 781 782 ## Quick Reference 783 784 ### Command Checklist 785 - [ ] Create `kamaji/commands/yourcommand.py` 786 - [ ] Export from `kamaji/commands/__init__.py` 787 - [ ] Register in `kamaji/cli.py` 788 - [ ] Add to `kamaji/autocomplete.py` (for TUI) 789 - [ ] Test with `kamaji yourcommand` 790 791 ### Agent Checklist 792 - [ ] Create `kamaji/agents/youragent.py` 793 - [ ] Export from `kamaji/agents/__init__.py` 794 - [ ] Add to `AgentRouter` in `kamaji/agents/router.py` 795 - [ ] Add keywords for auto-routing 796 - [ ] Add to autocomplete with @ prefix 797 - [ ] Test with `@youragent <task>` 798 799 ### Tool Checklist 800 - [ ] Add function to `kamaji/tools.py` 801 - [ ] Write clear docstring with examples 802 - [ ] Handle all error cases 803 - [ ] Test tool in isolation 804 - [ ] Register in agent's tools list 805 - [ ] Document input/output format 806 807 --- 808 809 ## Need Help? 810 811 Check existing examples: 812 - **Commands**: `kamaji/commands/mature.py`, `kamaji/commands/work.py` 813 - **Agents**: `kamaji/agents/code_review.py`, `kamaji/agents/documentation.py` 814 - **Tools**: `kamaji/tools.py` 815 816 Happy extending! 🚀