/ docs / integration / CREATING_EXTENSIONS.md
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! 🚀