opus-agent-base 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. opus_agent_base-0.1.0/.gitignore +2 -0
  2. opus_agent_base-0.1.0/.python-version +1 -0
  3. opus_agent_base-0.1.0/PKG-INFO +20 -0
  4. opus_agent_base-0.1.0/pyproject.toml +34 -0
  5. opus_agent_base-0.1.0/src/opus_agent_base/__init__.py +21 -0
  6. opus_agent_base-0.1.0/src/opus_agent_base/agent/__init__.py +1 -0
  7. opus_agent_base-0.1.0/src/opus_agent_base/agent/agent_builder.py +91 -0
  8. opus_agent_base-0.1.0/src/opus_agent_base/agent/agent_manager.py +157 -0
  9. opus_agent_base-0.1.0/src/opus_agent_base/agent/agent_runner.py +52 -0
  10. opus_agent_base-0.1.0/src/opus_agent_base/cli/__init__.py +1 -0
  11. opus_agent_base-0.1.0/src/opus_agent_base/cli/cli.py +293 -0
  12. opus_agent_base-0.1.0/src/opus_agent_base/common/__init__.py +1 -0
  13. opus_agent_base-0.1.0/src/opus_agent_base/common/datetime_helper.py +157 -0
  14. opus_agent_base-0.1.0/src/opus_agent_base/common/logging_config.py +177 -0
  15. opus_agent_base-0.1.0/src/opus_agent_base/config/__init__.py +1 -0
  16. opus_agent_base-0.1.0/src/opus_agent_base/config/config_command_manager.py +191 -0
  17. opus_agent_base-0.1.0/src/opus_agent_base/config/config_manager.py +102 -0
  18. opus_agent_base-0.1.0/src/opus_agent_base/config/nested_config_manager.py +102 -0
  19. opus_agent_base-0.1.0/src/opus_agent_base/model/__init__.py +1 -0
  20. opus_agent_base-0.1.0/src/opus_agent_base/model/model_manager.py +72 -0
  21. opus_agent_base-0.1.0/src/opus_agent_base/prompt/__init__.py +1 -0
  22. opus_agent_base-0.1.0/src/opus_agent_base/prompt/instructions_manager.py +67 -0
  23. opus_agent_base-0.1.0/src/opus_agent_base/tools/__init__.py +1 -0
  24. opus_agent_base-0.1.0/src/opus_agent_base/tools/custom_tool.py +28 -0
  25. opus_agent_base-0.1.0/src/opus_agent_base/tools/custom_tools_manager.py +40 -0
  26. opus_agent_base-0.1.0/src/opus_agent_base/tools/fastmcp_client_helper.py +63 -0
  27. opus_agent_base-0.1.0/src/opus_agent_base/tools/fastmcp_server_config.py +10 -0
  28. opus_agent_base-0.1.0/src/opus_agent_base/tools/higher_order_tool.py +29 -0
  29. opus_agent_base-0.1.0/src/opus_agent_base/tools/higher_order_tools_manager.py +44 -0
  30. opus_agent_base-0.1.0/src/opus_agent_base/tools/mcp_manager.py +109 -0
  31. opus_agent_base-0.1.0/src/opus_agent_base/tools/mcp_server_registry.py +49 -0
  32. opus_agent_base-0.1.0/src/opus_agent_base/tools/meta_tool.py +102 -0
  33. opus_agent_base-0.1.0/src/opus_agent_base/tools/meta_tools_manager.py +58 -0
  34. opus_agent_base-0.1.0/src/opus_agent_base/tools/openapi_meta_tool.py +311 -0
  35. opus_agent_base-0.1.0/src/opus_agent_base/ui/__init__.py +2 -0
  36. opus_agent_base-0.1.0/src/opus_agent_base/ui/logo.py +28 -0
@@ -0,0 +1,2 @@
1
+ __pycache__
2
+ dist
@@ -0,0 +1 @@
1
+ 3.13
@@ -0,0 +1,20 @@
1
+ Metadata-Version: 2.4
2
+ Name: opus-agent-base
3
+ Version: 0.1.0
4
+ Summary: Base framework for Opus AI Agents
5
+ Author-email: Sathish <sathish316@gmail.com>
6
+ Requires-Python: >=3.12
7
+ Requires-Dist: boto3>=1.40.21
8
+ Requires-Dist: chromadb>=1.1.1
9
+ Requires-Dist: fastmcp>=2.12.4
10
+ Requires-Dist: httpx>=0.24.0
11
+ Requires-Dist: prompt-toolkit>=3.0.0
12
+ Requires-Dist: pydantic-ai-slim[anthropic,openai,retries]>=0.8.1
13
+ Requires-Dist: pydantic-ai>=0.8.1
14
+ Requires-Dist: pyyaml>=6.0.0
15
+ Requires-Dist: rapidfuzz>=3.14.1
16
+ Requires-Dist: rich>=13.0.0
17
+ Requires-Dist: singleton-decorator>=1.0.0
18
+ Requires-Dist: tenacity>=8.2.0
19
+ Requires-Dist: tiktoken>=0.12.0
20
+ Requires-Dist: typer>=0.19.2
@@ -0,0 +1,34 @@
1
+ [project]
2
+ name = "opus-agent-base"
3
+ version = "0.1.0"
4
+ description = "Base framework for Opus AI Agents"
5
+ authors = [
6
+ { name = "Sathish", email = "sathish316@gmail.com" }
7
+ ]
8
+ requires-python = ">=3.12"
9
+ dependencies = [
10
+ "boto3>=1.40.21",
11
+ "pydantic-ai>=0.8.1",
12
+ "pydantic-ai-slim[anthropic,openai,retries]>=0.8.1",
13
+ "tenacity>=8.2.0",
14
+ "httpx>=0.24.0",
15
+ "singleton-decorator>=1.0.0",
16
+ "fastmcp>=2.12.4",
17
+ "typer>=0.19.2",
18
+ "tiktoken>=0.12.0",
19
+ "chromadb>=1.1.1",
20
+ "rapidfuzz>=3.14.1",
21
+ "prompt-toolkit>=3.0.0",
22
+ "rich>=13.0.0",
23
+ "pyyaml>=6.0.0",
24
+ ]
25
+
26
+ [project.scripts]
27
+ opus-agent-base = "opus_agent_base:main"
28
+
29
+ [build-system]
30
+ requires = ["hatchling"]
31
+ build-backend = "hatchling.build"
32
+
33
+ [tool.hatch.build.targets.wheel]
34
+ packages = ["src/opus_agent_base"]
@@ -0,0 +1,21 @@
1
+ """
2
+ Opus Agent Base - Core framework for building AI agents.
3
+ """
4
+
5
+ __version__ = "0.1.0"
6
+
7
+
8
+ def main() -> None:
9
+ """Entry point for opus-agent-base CLI."""
10
+ # Lazy import to avoid circular dependencies
11
+ from opus_agent_base.cli.cli import create_cli_app
12
+
13
+ app = create_cli_app(
14
+ agent_name="Opus Agent Base",
15
+ agent_description="Base framework for Opus AI Agents",
16
+ agent_version=__version__,
17
+ )
18
+ app()
19
+
20
+
21
+ __all__ = ["main", "__version__"]
@@ -0,0 +1 @@
1
+ """Agent management and execution modules."""
@@ -0,0 +1,91 @@
1
+ from opus_agent_base.config.config_manager import ConfigManager
2
+ from opus_agent_base.prompt.instructions_manager import InstructionsManager
3
+ from opus_agent_base.model.model_manager import ModelManager
4
+ from opus_agent_base.tools.custom_tool import CustomTool
5
+ from opus_agent_base.tools.higher_order_tool import HigherOrderTool
6
+ from opus_agent_base.tools.meta_tool import MetaTool
7
+ from opus_agent_base.tools.fastmcp_server_config import FastMCPServerConfig
8
+ from opus_agent_base.tools.mcp_manager import MCPManager
9
+
10
+ from typing import Any
11
+ from pydantic_ai import Agent
12
+
13
+
14
+ class AgentBuilder:
15
+ """Builder for the agent"""
16
+
17
+ def __init__(self, config_manager: ConfigManager):
18
+ self.config_manager = config_manager
19
+ self.custom_tools: list[CustomTool] = []
20
+ self.higher_order_tools: list[HigherOrderTool] = []
21
+ self.meta_tools: list[MetaTool] = []
22
+ self.mcp_servers_config: list[FastMCPServerConfig] = []
23
+
24
+ def name(self, name: str):
25
+ self.name = name
26
+ return self
27
+
28
+ def set_system_prompt_keys(self, system_prompt_keys: list[str]):
29
+ self.system_prompt_keys = system_prompt_keys
30
+ return self
31
+
32
+ def add_instructions_manager(
33
+ self, instructions_manager: InstructionsManager = None
34
+ ):
35
+ if instructions_manager is not None:
36
+ self.instructions_manager = instructions_manager
37
+ else:
38
+ self.instructions_manager = InstructionsManager()
39
+ return self
40
+
41
+ def instruction(self, key: str, file: str):
42
+ self.instructions_manager.put_from_file(key, file)
43
+ return self
44
+
45
+ def add_model_manager(self):
46
+ self.model_manager = ModelManager(self.config_manager)
47
+ return self
48
+
49
+ def custom_tool(self, custom_tool: CustomTool):
50
+ self.custom_tools.append(custom_tool)
51
+ return self
52
+
53
+ def higher_order_tool(self, higher_order_tool: HigherOrderTool):
54
+ self.higher_order_tools.append(higher_order_tool)
55
+ return self
56
+
57
+ def meta_tool(self, meta_tool: MetaTool):
58
+ self.meta_tools.append(meta_tool)
59
+ return self
60
+
61
+ def add_mcp_server_config(self, mcp_server_config: FastMCPServerConfig):
62
+ self.mcp_servers_config.append(mcp_server_config)
63
+ return self
64
+
65
+ def add_mcp_servers_config(self, mcp_servers_config: list[FastMCPServerConfig]):
66
+ self.mcp_servers_config.extend(mcp_servers_config)
67
+ return self
68
+
69
+ def set_deps_type(self, deps_type: Any):
70
+ self.deps_type = deps_type
71
+ return self
72
+
73
+ def set_output_type(self, output_type: Any):
74
+ self.output_type = output_type
75
+ return self
76
+
77
+ def build_simple_agent(self) -> Agent:
78
+ system_prompt = "\n".join(
79
+ self.instructions_manager.get(key) for key in self.system_prompt_keys
80
+ )
81
+ agent_kwargs = {
82
+ "system_prompt": system_prompt,
83
+ "model": self.model_manager.get_model(),
84
+ "tools": [],
85
+ }
86
+
87
+ # Add output_type if set
88
+ if hasattr(self, "output_type") and self.output_type is not None:
89
+ agent_kwargs["output_type"] = self.output_type
90
+
91
+ return Agent(**agent_kwargs)
@@ -0,0 +1,157 @@
1
+ import json
2
+ import logging
3
+
4
+ from fastmcp.client.client import ClientSession
5
+ from mcp.types import Tool as MCPTool
6
+ from pydantic_ai import Agent
7
+ from pydantic_ai.tools import Tool
8
+ from singleton_decorator import singleton
9
+
10
+ from opus_agent_base.agent.agent_builder import AgentBuilder
11
+ from opus_agent_base.common.logging_config import console_log
12
+ from opus_agent_base.tools.custom_tools_manager import CustomToolsManager
13
+ from opus_agent_base.tools.mcp_manager import MCPManager
14
+ from opus_agent_base.tools.higher_order_tools_manager import HigherOrderToolsManager
15
+ from opus_agent_base.tools.meta_tools_manager import MetaToolsManager
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ @singleton
21
+ class AgentManager:
22
+ """
23
+ Manager for the agent
24
+ """
25
+
26
+ def __init__(self, name: str, builder: AgentBuilder):
27
+ self.name = name
28
+ self.config_manager = builder.config_manager
29
+ self.system_prompt_keys = builder.system_prompt_keys
30
+ self.instructions_manager = builder.instructions_manager
31
+ self.model_manager = builder.model_manager
32
+ self.custom_tools = builder.custom_tools
33
+ self.higher_order_tools = builder.higher_order_tools
34
+ self.meta_tools = builder.meta_tools
35
+ self.mcp_servers_config = builder.mcp_servers_config
36
+
37
+ async def initialize_agent(self):
38
+ # System prompt
39
+ logger.info("Initializing Agent")
40
+ agent_system_prompt = "\n".join(
41
+ self.instructions_manager.get(key)
42
+ for key in self.system_prompt_keys
43
+ )
44
+
45
+ # Initialize MCP servers
46
+ await self.initialize_mcp_servers()
47
+
48
+ # Initialize Agent tools
49
+ await self.initialize_agent_tools()
50
+
51
+ # Initialize Agent
52
+ self.agent = Agent(
53
+ system_prompt=agent_system_prompt,
54
+ model=self.model_manager.get_model(),
55
+ tools=self.agent_tools,
56
+ )
57
+
58
+ # Add custom tools to Agent
59
+ self.custom_tools_manager = CustomToolsManager(
60
+ self.config_manager,
61
+ self.instructions_manager,
62
+ self.model_manager,
63
+ self.agent,
64
+ )
65
+ self.custom_tools_manager.initialize_tools(self.custom_tools)
66
+
67
+ # Add higher order tools to Agent
68
+ self.higher_order_tools_manager = HigherOrderToolsManager(
69
+ self.config_manager, self.agent, self.fastmcp_client_context
70
+ )
71
+ await self.higher_order_tools_manager.initialize_tools(self.higher_order_tools)
72
+
73
+ # Add meta tools to Agent
74
+ self.meta_tools_manager = MetaToolsManager(self.config_manager, self.agent)
75
+ meta_tools = await self.meta_tools_manager.initialize_tools(self.meta_tools)
76
+
77
+ logger.info("Agent initialized")
78
+
79
+ async def initialize_mcp_servers(self):
80
+ # Initialize MCP manager and MCP servers
81
+ logger.info("Initializing MCP servers")
82
+ self.mcp_manager = MCPManager(self.config_manager)
83
+ self.mcp_manager.add_mcp_servers(self.mcp_servers_config)
84
+ logger.info("MCP servers initialized")
85
+
86
+ async def initialize_agent_tools(self):
87
+ # Initialize Agent tools
88
+ self.agent_tools = []
89
+ self.fastmcp_client_context = await self.mcp_manager.initialize_fastmcp_client_context()
90
+ await self.mcp_manager.inspect_fastmcp_client_tools()
91
+
92
+ async def tools_initializer(session: ClientSession):
93
+ enabled_tools = {}
94
+ logger.info("Initializing agent tools")
95
+ client_tools = await session.list_tools()
96
+ for tool in client_tools:
97
+ tool_prefix = tool.name.split("_")[0]
98
+ if tool_prefix in self.config_manager.get_setting(
99
+ "mcp_config.allowed_tool_prefixes", []
100
+ ):
101
+ if tool.name in self.config_manager.get_setting(
102
+ "mcp_config.allowed_tools"
103
+ ).get(tool_prefix, []):
104
+ logger.debug(f"Wrapping FastMCP tool: {tool.name}")
105
+ self.agent_tools.append(self._wrap_tool(tool, self.fastmcp_client_context))
106
+ self._log_enabled_tools(enabled_tools, tool_prefix, tool.name)
107
+ else:
108
+ self._log_enabled_tools(enabled_tools, tool_prefix, tool.name)
109
+ self.agent_tools.append(self._wrap_tool(tool, self.fastmcp_client_context))
110
+ return enabled_tools
111
+
112
+ if self.fastmcp_client_context is not None:
113
+ result = await self.fastmcp_client_context(tools_initializer)
114
+ console_log(f"Enabled tools: {result}")
115
+
116
+ # Initialize Meta tools
117
+ result = await self.initialize_meta_tools()
118
+ logger.info(f"Enabled Meta tools: {result}")
119
+
120
+ async def initialize_meta_tools(self):
121
+ # Initialize Meta tools
122
+ result = []
123
+ logger.info("Initializing Meta tools")
124
+ for meta_tool in self.meta_tools:
125
+ await meta_tool.setup_tool()
126
+ agent_tool = await meta_tool.build_agent_tool()
127
+ self.agent_tools.append(agent_tool)
128
+ result.append(meta_tool.name)
129
+
130
+ logger.info("Meta tools initialized")
131
+ return result
132
+
133
+ def get_agent(self):
134
+ return self.agent
135
+
136
+ def _wrap_tool(self, tool: MCPTool, fastmcp_client_context) -> Tool:
137
+ async def mcp_tool_function(**kwargs):
138
+ """Dynamically created tool function for MCP tool"""
139
+
140
+ async def execute_tool(client: ClientSession):
141
+ result = await client.call_tool(tool.name, kwargs)
142
+ return result
143
+
144
+ result = await fastmcp_client_context(execute_tool)
145
+ return result
146
+
147
+ return Tool.from_schema(
148
+ name=tool.name,
149
+ description=tool.description,
150
+ json_schema=tool.inputSchema,
151
+ function=mcp_tool_function,
152
+ )
153
+
154
+ def _log_enabled_tools(self, enabled_tools, tool_prefix, tool_name):
155
+ if tool_prefix not in enabled_tools:
156
+ enabled_tools[tool_prefix] = set()
157
+ enabled_tools[tool_prefix].add(tool_name)
@@ -0,0 +1,52 @@
1
+ import logging
2
+
3
+ from opus_agent_base.agent.agent_builder import AgentBuilder
4
+ from opus_agent_base.agent.agent_manager import AgentManager
5
+ from opus_agent_base.common.logging_config import console_log, quick_setup
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+
10
+ class AgentRunner:
11
+ def __init__(
12
+ self,
13
+ agent_builder: AgentBuilder,
14
+ ):
15
+ self.agent_builder = agent_builder
16
+ self.agent_manager = None
17
+ self.agent = None
18
+ self._setup_logging()
19
+
20
+ def _setup_logging(self):
21
+ log_level = self.agent_builder.config_manager.get_setting("debug.log_level", "ERROR")
22
+ quick_setup(log_level=log_level)
23
+
24
+ async def run_agent(self):
25
+ """Run Agent CLI using PydanticAI CLI"""
26
+ try:
27
+ console_log("🚀 Starting Agent...")
28
+ # Initialize Agent
29
+ if self.agent is None:
30
+ self.agent_manager = AgentManager(
31
+ self.agent_builder.name,
32
+ self.agent_builder
33
+ )
34
+ await self.agent_manager.initialize_agent()
35
+ self.agent = self.agent_manager.get_agent()
36
+
37
+ console_log("✅ Agent started")
38
+ await self.agent.to_cli()
39
+
40
+ except ExceptionGroup as eg:
41
+ console_log("❌ Exception running agent")
42
+ logger.error("Exception running agent:")
43
+ for e in eg.exceptions:
44
+ import traceback
45
+ traceback.print_exception(e)
46
+ raise
47
+ except KeyboardInterrupt:
48
+ console_log("\n🛑 Agent interrupted")
49
+ logger.debug("Agent interrupted by user")
50
+ finally:
51
+ console_log("🏁 Agent session ended")
52
+ logger.debug("Agent session ended")
@@ -0,0 +1 @@
1
+ """CLI interface for Opus agents."""
@@ -0,0 +1,293 @@
1
+ import asyncio
2
+ import logging
3
+ import os
4
+ import sys
5
+ from pathlib import Path
6
+
7
+ import typer
8
+ from prompt_toolkit import PromptSession
9
+ from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
10
+ from prompt_toolkit.completion import WordCompleter
11
+ from prompt_toolkit.history import FileHistory
12
+ from prompt_toolkit.styles import Style
13
+ from rich.console import Console
14
+ from rich.table import Table
15
+
16
+ from opus_agent_base.config.config_command_manager import ConfigCommandManager
17
+ from opus_agent_base.config.config_manager import ConfigManager
18
+ from opus_agent_base.ui.logo import display_logo
19
+
20
+ # Setup rich console for pretty output
21
+ console = Console()
22
+ logger = logging.getLogger(__name__)
23
+
24
+ config_manager = ConfigManager()
25
+ config_command_manager = ConfigCommandManager(config_manager, console)
26
+
27
+
28
+ def create_cli_app(
29
+ agent_name: str, agent_description: str, agent_version: str, agent_runner: callable = None
30
+ ) -> typer.Typer:
31
+ """Creates a Typer app for the agent CLI."""
32
+ app = typer.Typer(
33
+ name=agent_name,
34
+ help=agent_description,
35
+ rich_markup_mode="rich",
36
+ )
37
+
38
+ def run_cli_mode(run_agent_on_startup: bool = False):
39
+ """Run the admin mode with slash commands."""
40
+ display_logo(console)
41
+ # Set up command history file
42
+ history_dir = Path.home() / ".opusai"
43
+ history_dir.mkdir(exist_ok=True)
44
+ history_file = history_dir / ".admin_history"
45
+
46
+ # Set up command completion for all available commands and subcommands
47
+ command_completer = WordCompleter(
48
+ [
49
+ "/help",
50
+ "/agent",
51
+ "/todo-agent",
52
+ "/sde-agent",
53
+ "/config",
54
+ "/config init",
55
+ "/config list",
56
+ "/config get",
57
+ "/config set",
58
+ "/config delete",
59
+ "/status",
60
+ "/clear",
61
+ "/exit",
62
+ "/quit",
63
+ ],
64
+ ignore_case=True,
65
+ sentence=True,
66
+ )
67
+
68
+ # Create custom style for prompt
69
+ prompt_style = Style.from_dict(
70
+ {
71
+ "prompt": "bold ansiblue",
72
+ }
73
+ )
74
+
75
+ # Create prompt session with history, auto-suggestions, and completion
76
+ session = PromptSession(
77
+ history=FileHistory(str(history_file)),
78
+ auto_suggest=AutoSuggestFromHistory(),
79
+ completer=command_completer,
80
+ complete_while_typing=True,
81
+ style=prompt_style,
82
+ )
83
+
84
+ # Run agent on startup if requested
85
+ if run_agent_on_startup:
86
+ try:
87
+ if agent_runner:
88
+ asyncio.run(agent_runner())
89
+ else:
90
+ console.print("[yellow]No agent runner provided[/yellow]")
91
+ except KeyboardInterrupt:
92
+ console.print("\n[dim]Agent interrupted. Returning to CLI...[/dim]")
93
+ except Exception as e:
94
+ import traceback
95
+
96
+ console.print(f"[red]Agent error: {e}[/red]")
97
+ console.print("[dim]Stacktrace:[/dim]")
98
+ console.print(traceback.format_exc())
99
+
100
+ while True:
101
+ try:
102
+ # Use prompt_toolkit for input with command history support
103
+ command = session.prompt([("class:prompt", "\nopus> ")]).strip()
104
+
105
+ if not command:
106
+ continue
107
+
108
+ if not command.startswith("/"):
109
+ console.print(
110
+ "[yellow]Commands must start with '/'. Type /help for available commands.[/yellow]"
111
+ )
112
+ continue
113
+
114
+ # Parse command
115
+ parts = command[1:].split()
116
+ if not parts:
117
+ continue
118
+
119
+ cmd = parts[0].lower()
120
+ args = parts[1:] if len(parts) > 1 else []
121
+
122
+ # Handle commands
123
+ if cmd == "exit" or cmd == "quit":
124
+ console.print("[green]Goodbye![/green]")
125
+ break
126
+ elif cmd == "help":
127
+ show_admin_help()
128
+ elif cmd == "agent":
129
+ if agent_runner:
130
+ asyncio.run(agent_runner())
131
+ else:
132
+ console.print("[yellow]No agent runner configured[/yellow]")
133
+ elif cmd == "todo-agent":
134
+ from opus_todo_agent.todo_agent_runner import run_todo_agent
135
+
136
+ asyncio.run(run_todo_agent())
137
+ elif cmd == "sde-agent":
138
+ from opus_sde_agent.sde_agent_runner import run_sde_agent
139
+
140
+ asyncio.run(run_sde_agent())
141
+ elif cmd == "config":
142
+ config_command_manager.handle_config_command(args)
143
+ elif cmd == "status":
144
+ show_status()
145
+ elif cmd == "clear":
146
+ os.system("clear" if os.name == "posix" else "cls")
147
+ else:
148
+ console.print(f"[red]Unknown command: /{cmd}[/red]")
149
+ console.print("Type [bold]/help[/bold] for available commands.")
150
+
151
+ except KeyboardInterrupt:
152
+ console.print("\n[green]Goodbye![/green]")
153
+ break
154
+ except EOFError:
155
+ console.print("\n[green]Goodbye![/green]")
156
+ break
157
+ except Exception as e:
158
+ console.print(f"[red]Error: {e}[/red]")
159
+ sys.exit(1)
160
+
161
+ def show_admin_help():
162
+ """Show help for admin commands."""
163
+ table = Table(title="Admin Mode Commands")
164
+ table.add_column("Command", style="cyan", no_wrap=True)
165
+ table.add_column("Description", style="white")
166
+
167
+ commands = [
168
+ ("/help", "Show this help message"),
169
+ ("/agent", "Run Agent mode"),
170
+ ("/config init", "Initialize config file from sample template"),
171
+ ("/config list", "List all configuration settings"),
172
+ ("/config get <key>", "Get a specific configuration setting"),
173
+ (
174
+ "/config set <key> <value>",
175
+ "Set a configuration setting",
176
+ ),
177
+ ("/config delete <key>", "Delete a configuration setting"),
178
+ ("/status", "Show agent status and configuration"),
179
+ ("/clear", "Clear the screen"),
180
+ ("/exit, /quit", "Exit admin mode"),
181
+ ]
182
+
183
+ for cmd, desc in commands:
184
+ table.add_row(cmd, desc)
185
+
186
+ console.print(table)
187
+
188
+ # Add helpful tips
189
+ console.print("\n[bold cyan]💡 Navigation & Editing Tips:[/bold cyan]")
190
+ tips_table = Table(show_header=False, box=None, padding=(0, 2))
191
+ tips_table.add_column("Key", style="yellow")
192
+ tips_table.add_column("Action", style="dim white")
193
+
194
+ tips = [
195
+ ("↑/↓", "Navigate command history"),
196
+ ("Tab", "Auto-complete commands"),
197
+ ("→", "Accept auto-suggestion"),
198
+ ("Ctrl+C", "Cancel / Exit"),
199
+ ("Ctrl+L", "Clear screen"),
200
+ ("Ctrl+A/E", "Jump to start/end of line"),
201
+ ]
202
+
203
+ for key, action in tips:
204
+ tips_table.add_row(key, action)
205
+
206
+ console.print(tips_table)
207
+ console.print(
208
+ "\n[dim]Config keys support dot notation (e.g., todoist.api_key)[/dim]"
209
+ )
210
+
211
+ def show_status():
212
+ """Show agent status and configuration."""
213
+ table = Table(title="Agent Status")
214
+ table.add_column("Component", style="cyan")
215
+ table.add_column("Status", style="white")
216
+
217
+ # Check environment variables
218
+ required_env_vars = [
219
+ "OPENAI_API_KEY",
220
+ "ANTHROPIC_API_KEY",
221
+ "GOOGLE_OAUTH_CLIENT_ID",
222
+ "GOOGLE_OAUTH_CLIENT_SECRET",
223
+ "SLACK_MCP_XOXC_TOKEN",
224
+ "SLACK_MCP_XOXD_TOKEN",
225
+ "SLACK_MCP_XOXP_TOKEN",
226
+ ]
227
+
228
+ for var in required_env_vars:
229
+ value = os.getenv(var)
230
+ status = "[green]Set[/green]" if value else "[red]Not Set[/red]"
231
+ table.add_row(var, status)
232
+
233
+ # Configuration file status
234
+ config_exists = config_manager.config_file.exists()
235
+ config_status = (
236
+ "[green]Exists[/green]" if config_exists else "[yellow]Not Found[/yellow]"
237
+ )
238
+ table.add_row("Config File", config_status)
239
+ table.add_row("Config Path", str(config_manager.config_file))
240
+
241
+ if config_exists:
242
+ config = config_manager.load_config()
243
+ table.add_row("Config Settings", str(len(config)))
244
+
245
+ console.print(table)
246
+
247
+ @app.command()
248
+ def main(
249
+ agent: bool = typer.Option(
250
+ False,
251
+ "--agent",
252
+ "-ai",
253
+ help="Start in Agent mode with slash commands for configuration",
254
+ ),
255
+ todo: bool = typer.Option(
256
+ False,
257
+ "--todo-agent",
258
+ "-todo",
259
+ help="Start in TODO Agent mode",
260
+ ),
261
+ sde: bool = typer.Option(
262
+ False,
263
+ "--sde-agent",
264
+ "-sde",
265
+ help="Start in SDE Agent mode",
266
+ ),
267
+ admin: bool = typer.Option(
268
+ False,
269
+ "--admin",
270
+ "-a",
271
+ help="Start in admin mode with slash commands for configuration",
272
+ ),
273
+ version: bool = typer.Option(
274
+ False, "--version", "-v", help="Show version information"
275
+ ),
276
+ ):
277
+ f"""
278
+ {agent_name} - {agent_description}.
279
+
280
+ Use --admin to enter configuration mode with slash commands.
281
+ """
282
+ if version:
283
+ console.print(f"[bold blue]{agent_name}[/bold blue] v{agent_version}")
284
+ console.print(agent_description)
285
+ return
286
+
287
+ if admin:
288
+ run_cli_mode(run_agent_on_startup=False)
289
+ else:
290
+ # Run the configured agent (if provided) or enter CLI mode
291
+ run_cli_mode(run_agent_on_startup=True)
292
+
293
+ return app
@@ -0,0 +1 @@
1
+ """Common utilities and helpers."""