a4e 0.1.5__py3-none-any.whl

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 (70) hide show
  1. a4e/__init__.py +0 -0
  2. a4e/cli.py +47 -0
  3. a4e/cli_commands/__init__.py +5 -0
  4. a4e/cli_commands/add.py +376 -0
  5. a4e/cli_commands/deploy.py +149 -0
  6. a4e/cli_commands/dev.py +162 -0
  7. a4e/cli_commands/info.py +206 -0
  8. a4e/cli_commands/init.py +211 -0
  9. a4e/cli_commands/list.py +227 -0
  10. a4e/cli_commands/mcp.py +504 -0
  11. a4e/cli_commands/remove.py +197 -0
  12. a4e/cli_commands/update.py +285 -0
  13. a4e/cli_commands/validate.py +117 -0
  14. a4e/core.py +109 -0
  15. a4e/dev_runner.py +425 -0
  16. a4e/server.py +86 -0
  17. a4e/templates/agent.md.j2 +168 -0
  18. a4e/templates/agent.py.j2 +15 -0
  19. a4e/templates/agents.md.j2 +99 -0
  20. a4e/templates/metadata.json.j2 +20 -0
  21. a4e/templates/prompt.md.j2 +20 -0
  22. a4e/templates/prompts/agent.md.j2 +206 -0
  23. a4e/templates/skills/agents.md.j2 +110 -0
  24. a4e/templates/skills/skill.md.j2 +120 -0
  25. a4e/templates/support_module.py.j2 +84 -0
  26. a4e/templates/tool.py.j2 +60 -0
  27. a4e/templates/tools/agent.md.j2 +192 -0
  28. a4e/templates/view.tsx.j2 +21 -0
  29. a4e/templates/views/agent.md.j2 +219 -0
  30. a4e/tools/__init__.py +70 -0
  31. a4e/tools/agent_tools/__init__.py +12 -0
  32. a4e/tools/agent_tools/add_support_module.py +95 -0
  33. a4e/tools/agent_tools/add_tool.py +115 -0
  34. a4e/tools/agent_tools/list_tools.py +28 -0
  35. a4e/tools/agent_tools/remove_tool.py +69 -0
  36. a4e/tools/agent_tools/update_tool.py +123 -0
  37. a4e/tools/deploy/__init__.py +8 -0
  38. a4e/tools/deploy/deploy.py +59 -0
  39. a4e/tools/dev/__init__.py +10 -0
  40. a4e/tools/dev/check_environment.py +79 -0
  41. a4e/tools/dev/dev_start.py +30 -0
  42. a4e/tools/dev/dev_stop.py +26 -0
  43. a4e/tools/project/__init__.py +10 -0
  44. a4e/tools/project/get_agent_info.py +66 -0
  45. a4e/tools/project/get_instructions.py +216 -0
  46. a4e/tools/project/initialize_project.py +231 -0
  47. a4e/tools/schemas/__init__.py +8 -0
  48. a4e/tools/schemas/generate_schemas.py +278 -0
  49. a4e/tools/skills/__init__.py +12 -0
  50. a4e/tools/skills/add_skill.py +105 -0
  51. a4e/tools/skills/helpers.py +137 -0
  52. a4e/tools/skills/list_skills.py +54 -0
  53. a4e/tools/skills/remove_skill.py +74 -0
  54. a4e/tools/skills/update_skill.py +150 -0
  55. a4e/tools/validation/__init__.py +8 -0
  56. a4e/tools/validation/validate.py +389 -0
  57. a4e/tools/views/__init__.py +12 -0
  58. a4e/tools/views/add_view.py +40 -0
  59. a4e/tools/views/helpers.py +91 -0
  60. a4e/tools/views/list_views.py +27 -0
  61. a4e/tools/views/remove_view.py +73 -0
  62. a4e/tools/views/update_view.py +124 -0
  63. a4e/utils/dev_manager.py +253 -0
  64. a4e/utils/schema_generator.py +255 -0
  65. a4e-0.1.5.dist-info/METADATA +427 -0
  66. a4e-0.1.5.dist-info/RECORD +70 -0
  67. a4e-0.1.5.dist-info/WHEEL +5 -0
  68. a4e-0.1.5.dist-info/entry_points.txt +2 -0
  69. a4e-0.1.5.dist-info/licenses/LICENSE +21 -0
  70. a4e-0.1.5.dist-info/top_level.txt +1 -0
@@ -0,0 +1,162 @@
1
+ # a4e/cli_commands/dev.py
2
+
3
+ import typer
4
+ from pathlib import Path
5
+ from typing import Optional
6
+ import pyperclip
7
+ import os
8
+
9
+
10
+ from ..utils.dev_manager import DevManager # Correct relative import
11
+
12
+
13
+ # Create a 'Typer' app specifically for the 'dev' command group
14
+ app = typer.Typer(
15
+ no_args_is_help=True,
16
+ help="Commands for running the development server and ngrok tunnels.",
17
+ )
18
+
19
+
20
+ def get_ngrok_authtoken():
21
+ """
22
+ Attempts to retrieve the ngrok authtoken from the system.
23
+ """
24
+ # Try to get the ngrok token from an environment variable
25
+ token = os.environ.get("NGROK_AUTHTOKEN")
26
+ if token:
27
+ return token
28
+
29
+ # If not an environment variable try to look for it in the system
30
+ try:
31
+ home = Path.home()
32
+ config_paths = [
33
+ home / "AppData" / "Local" / "ngrok" / "ngrok.yml", # Windows
34
+ home / ".ngrok2" / "ngrok.yml", # Linux
35
+ home / ".config" / "ngrok" / "ngrok.yml", # Linux
36
+ home / "Library" / "Application Support" / "ngrok" / "ngrok.yml", # macOS
37
+ ]
38
+ for config_path in config_paths:
39
+ if config_path.exists():
40
+ with open(config_path, "r") as f:
41
+ for line in f:
42
+ if line.strip().startswith("authtoken:"):
43
+ return line.strip().split(":", 1)[1].strip().strip("'\"")
44
+ except Exception:
45
+ pass
46
+
47
+ return None
48
+
49
+
50
+ @app.command()
51
+ def start(
52
+ directory: str = typer.Option(None, help="The directory to run the server on."),
53
+ port: int = typer.Option(5000, help="The local port to run the server on."),
54
+ auth_token: Optional[str] = typer.Option(
55
+ None,
56
+ help="Your ngrok authtoken. If not provided, it will be sourced from your environment or ngrok config.",
57
+ ),
58
+ ) -> None:
59
+ """
60
+ Starts the development server with an ngrok tunnel.
61
+ This command must be run from within the 'agent-store' directory.
62
+ """
63
+ if not auth_token:
64
+ auth_token = get_ngrok_authtoken()
65
+
66
+ if not auth_token:
67
+ print("Error: Could not find ngrok authtoken.")
68
+ print(
69
+ "Please provide it with the --auth-token option, or run 'ngrok config add-authtoken <YOUR_TOKEN>'"
70
+ )
71
+ raise typer.Exit(code=1)
72
+
73
+ current_dir = Path.cwd()
74
+ if directory:
75
+ current_dir = Path(directory)
76
+ project_dir = None
77
+
78
+ # Check if we're inside an agent directory (has agent.py and metadata.json)
79
+ if (current_dir / "agent.py").exists() and (current_dir / "metadata.json").exists():
80
+ # We're inside an agent directory - use it directly
81
+ project_dir = current_dir
82
+ print(f"Detected agent directory: {project_dir.name}")
83
+ elif current_dir.name == "agent-store":
84
+ # We're in agent-store - list available agents and prompt for selection
85
+ agent_store_path = current_dir
86
+ available_agents = []
87
+ if agent_store_path.is_dir():
88
+ available_agents = [d for d in agent_store_path.iterdir() if d.is_dir()]
89
+
90
+ while not project_dir:
91
+ print("\nSelect an agent to start:")
92
+ if available_agents:
93
+ for i, agent in enumerate(available_agents):
94
+ print(f" [{i + 1}] {agent.name}")
95
+ prompt_text = "\nPlease choose an agent"
96
+ else:
97
+ print("No agents found in the current 'agent-store' directory.")
98
+ raise typer.Exit(code=1)
99
+
100
+ try:
101
+ # Check if user entered a number
102
+ response = typer.prompt(prompt_text, type=str)
103
+ choice_index = int(response) - 1
104
+ if 0 <= choice_index < len(available_agents):
105
+ project_dir = available_agents[choice_index]
106
+ else:
107
+ print("Invalid number. Please try again.")
108
+ except ValueError:
109
+ # User entered a path string
110
+ print("Please enter a valid number.")
111
+ else:
112
+ print("Error: Run this command from an agent directory or the 'agent-store' directory.")
113
+ print(f"Current directory: {current_dir}")
114
+ print("\nTip: cd into your agent folder, or use --directory to specify the path.")
115
+ raise typer.Exit(code=1)
116
+
117
+ # Final validation of the selected directory
118
+ if not project_dir or not project_dir.is_dir():
119
+ print("Error: No valid agent directory selected.")
120
+ raise typer.Exit(code=1)
121
+
122
+ print(f"\nUsing agent folder: {project_dir}")
123
+ print("Starting development server...")
124
+
125
+ result = DevManager.start_dev_server(
126
+ project_dir=project_dir, port=port, auth_token=auth_token
127
+ )
128
+
129
+ if result.get("success"):
130
+ print("Dev mode started successfully:")
131
+ print(f" Public URL: {result.get('public_url')}")
132
+ print(f" Hub URL: {result.get('hub_url')}")
133
+
134
+ try:
135
+ pyperclip.copy(str(result.get("hub_url")))
136
+ print(" (Hub URL copied to clipboard!)")
137
+ except pyperclip.PyperclipException:
138
+ print(
139
+ " (Could not copy Hub URL to clipboard. Please install xclip/xsel or enable Wayland clipboard for Linux.)"
140
+ )
141
+
142
+ print("\nInstructions:")
143
+ for instruction in result.get("instructions", []):
144
+ print(f" {instruction}")
145
+
146
+ # Keep the CLI running to maintain the server process
147
+ print("\n[Server running - Press Ctrl+C to stop]")
148
+ try:
149
+ import time
150
+ while True:
151
+ time.sleep(1)
152
+ except KeyboardInterrupt:
153
+ print("\nStopping dev server...")
154
+ DevManager.stop_dev_server(port)
155
+ print("Dev server stopped.")
156
+ else:
157
+ print(f"Error starting server: {result.get('error')}")
158
+ if result.get("details"):
159
+ print(f"Details: {result.get('details')}")
160
+ if result.get("fix"):
161
+ print(f"Fix: {result.get('fix')}")
162
+ raise typer.Exit(code=1)
@@ -0,0 +1,206 @@
1
+ # a4e/cli_commands/info.py
2
+ """
3
+ Command for displaying agent information.
4
+ """
5
+
6
+ import typer
7
+ import json
8
+ from pathlib import Path
9
+ from typing import Optional
10
+
11
+ from rich.console import Console
12
+ from rich.panel import Panel
13
+ from rich.table import Table
14
+
15
+ console = Console()
16
+
17
+ # Create a 'Typer' app for the 'info' command
18
+ app = typer.Typer(
19
+ no_args_is_help=False,
20
+ help="Display information about your agent.",
21
+ )
22
+
23
+
24
+ def find_agent_dir(agent_name: Optional[str] = None) -> Optional[Path]:
25
+ """Find the agent directory from name or current working directory."""
26
+ cwd = Path.cwd()
27
+
28
+ if agent_name:
29
+ if Path(agent_name).is_absolute() and Path(agent_name).exists():
30
+ return Path(agent_name)
31
+ if (cwd / agent_name).exists():
32
+ return cwd / agent_name
33
+ agent_store = cwd / "file-store" / "agent-store" / agent_name
34
+ if agent_store.exists():
35
+ return agent_store
36
+ return None
37
+
38
+ if (cwd / "agent.py").exists() and (cwd / "metadata.json").exists():
39
+ return cwd
40
+
41
+ return None
42
+
43
+
44
+ @app.callback(invoke_without_command=True)
45
+ def info(
46
+ ctx: typer.Context,
47
+ agent_name: Optional[str] = typer.Option(
48
+ None, "--agent", "-a", help="Agent name/path (defaults to current directory)"
49
+ ),
50
+ json_output: bool = typer.Option(
51
+ False, "--json", "-j", help="Output as JSON"
52
+ ),
53
+ ) -> None:
54
+ """
55
+ Display information about the agent.
56
+
57
+ Shows metadata, structure, and statistics about the agent.
58
+
59
+ Example:
60
+ a4e info
61
+ a4e info --agent my-agent --json
62
+ """
63
+ # If a subcommand is being invoked, skip the callback logic
64
+ if ctx.invoked_subcommand is not None:
65
+ return
66
+
67
+ agent_dir = find_agent_dir(agent_name)
68
+ if not agent_dir:
69
+ console.print("[red]Error: Not in an agent directory. Use --agent to specify the agent.[/red]")
70
+ raise typer.Exit(code=1)
71
+
72
+ try:
73
+ from ..tools.project.get_agent_info import get_agent_info as mcp_get_agent_info
74
+
75
+ result = mcp_get_agent_info(agent_name=str(agent_dir))
76
+
77
+ # Handle response (get_agent_info returns {"agent_id", "metadata", "path"} or {"error"})
78
+ if "error" not in result:
79
+ if json_output:
80
+ console.print(json.dumps(result, indent=2))
81
+ return
82
+
83
+ # Display panel with basic info
84
+ metadata = result.get("metadata", {})
85
+
86
+ console.print(Panel.fit(
87
+ f"[bold cyan]{metadata.get('display_name', agent_dir.name)}[/bold cyan]\n"
88
+ f"[dim]{metadata.get('description', 'No description')}[/dim]\n\n"
89
+ f"[bold]ID:[/bold] {metadata.get('id', agent_dir.name)}\n"
90
+ f"[bold]Category:[/bold] {metadata.get('category', 'Unknown')}\n"
91
+ f"[bold]Path:[/bold] {agent_dir}",
92
+ title="Agent Info",
93
+ border_style="blue"
94
+ ))
95
+
96
+ # Structure summary
97
+ structure = result.get("structure", {})
98
+
99
+ table = Table(title="Structure Summary")
100
+ table.add_column("Component", style="cyan")
101
+ table.add_column("Count", justify="right")
102
+
103
+ table.add_row("Tools", str(structure.get("tools_count", 0)))
104
+ table.add_row("Views", str(structure.get("views_count", 0)))
105
+ table.add_row("Skills", str(structure.get("skills_count", 0)))
106
+ table.add_row("Prompts", str(structure.get("prompts_count", 0)))
107
+
108
+ console.print("")
109
+ console.print(table)
110
+
111
+ # List items if present
112
+ if structure.get("tools"):
113
+ console.print("\n[bold]Tools:[/bold]")
114
+ for tool in structure.get("tools", []):
115
+ console.print(f" • {tool}")
116
+
117
+ if structure.get("views"):
118
+ console.print("\n[bold]Views:[/bold]")
119
+ for view in structure.get("views", []):
120
+ console.print(f" • {view}")
121
+
122
+ if structure.get("skills"):
123
+ console.print("\n[bold]Skills:[/bold]")
124
+ for skill in structure.get("skills", []):
125
+ console.print(f" • {skill}")
126
+
127
+ else:
128
+ console.print(f"[red]Error: {result.get('error')}[/red]")
129
+ raise typer.Exit(code=1)
130
+
131
+ except ImportError as e:
132
+ console.print(f"[red]Error importing tools: {e}[/red]")
133
+ raise typer.Exit(code=1)
134
+ except Exception as e:
135
+ console.print(f"[red]Error: {e}[/red]")
136
+ raise typer.Exit(code=1)
137
+
138
+
139
+ @app.command("instructions")
140
+ def instructions(
141
+ json_output: bool = typer.Option(
142
+ False, "--json", "-j", help="Output as JSON"
143
+ ),
144
+ ) -> None:
145
+ """
146
+ Show instructions for AI agents on how to use A4E tools.
147
+
148
+ This displays the comprehensive documentation that AI assistants
149
+ (like Claude, Cursor, etc.) use to understand how to create A4E agents.
150
+
151
+ Example:
152
+ a4e info instructions
153
+ a4e info instructions --json
154
+ """
155
+ try:
156
+ from ..tools.project.get_instructions import get_instructions as mcp_get_instructions
157
+
158
+ result = mcp_get_instructions()
159
+
160
+ if result.get("success"):
161
+ if json_output:
162
+ console.print(json.dumps(result, indent=2))
163
+ return
164
+
165
+ # Display the instructions with nice formatting
166
+ console.print(Panel.fit(
167
+ "[bold cyan]A4E Agent Creator - AI Instructions[/bold cyan]\n\n"
168
+ "These instructions are provided to AI assistants when they use\n"
169
+ "the A4E MCP tools to create agents.",
170
+ border_style="blue"
171
+ ))
172
+
173
+ # Display quick reference
174
+ quick_ref = result.get("quick_reference", {})
175
+
176
+ table = Table(title="Quick Reference")
177
+ table.add_column("Item", style="cyan")
178
+ table.add_column("Value")
179
+
180
+ table.add_row("Workflow", " → ".join(quick_ref.get("workflow", [])))
181
+ table.add_row("Parameter Types", ", ".join(quick_ref.get("parameter_types", [])))
182
+ table.add_row("Templates", ", ".join(quick_ref.get("templates", [])))
183
+
184
+ naming = quick_ref.get("naming", {})
185
+ table.add_row("agent_name", naming.get("agent_name", ""))
186
+ table.add_row("tool_name", naming.get("tool_name", ""))
187
+ table.add_row("view_id", naming.get("view_id", ""))
188
+ table.add_row("skill_id", naming.get("skill_id", ""))
189
+
190
+ console.print("")
191
+ console.print(table)
192
+
193
+ # Print the full instructions
194
+ console.print("\n[bold]Full Instructions:[/bold]\n")
195
+ console.print(result.get("instructions", ""))
196
+
197
+ else:
198
+ console.print(f"[red]Error: {result.get('error')}[/red]")
199
+ raise typer.Exit(code=1)
200
+
201
+ except ImportError as e:
202
+ console.print(f"[red]Error importing tools: {e}[/red]")
203
+ raise typer.Exit(code=1)
204
+ except Exception as e:
205
+ console.print(f"[red]Error: {e}[/red]")
206
+ raise typer.Exit(code=1)
@@ -0,0 +1,211 @@
1
+ # a4e/cli_commands/init.py
2
+ """
3
+ Interactive project initialization command.
4
+ """
5
+
6
+ import typer
7
+ from pathlib import Path
8
+ from typing import Optional
9
+
10
+ from rich.console import Console
11
+ from rich.panel import Panel
12
+ from rich.prompt import Prompt, Confirm
13
+
14
+ console = Console()
15
+
16
+ # Create a 'Typer' app for the 'init' command
17
+ app = typer.Typer(
18
+ no_args_is_help=False,
19
+ help="Initialize a new A4E agent project with an interactive wizard.",
20
+ )
21
+
22
+ CATEGORIES = [
23
+ "Concierge",
24
+ "E-commerce",
25
+ "Fitness & Health",
26
+ "Education",
27
+ "Entertainment",
28
+ "Productivity",
29
+ "Finance",
30
+ "Customer Support",
31
+ "General",
32
+ ]
33
+
34
+ TEMPLATES = {
35
+ "basic": "Minimal files only (agent.py, metadata.json, welcome view)",
36
+ "with-tools": "Basic + example tool",
37
+ "with-views": "Basic + example view",
38
+ "full": "Basic + example tool + example view",
39
+ }
40
+
41
+
42
+ def validate_agent_name(name: str) -> bool:
43
+ """Validate agent name format (lowercase, hyphens/underscores only)."""
44
+ return name.replace("-", "").replace("_", "").isalnum() and name.islower()
45
+
46
+
47
+ @app.callback(invoke_without_command=True)
48
+ def init(
49
+ ctx: typer.Context,
50
+ name: Optional[str] = typer.Option(
51
+ None, "--name", "-n", help="Agent ID (lowercase, hyphens, e.g., 'nutrition-coach')"
52
+ ),
53
+ display_name: Optional[str] = typer.Option(
54
+ None, "--display-name", "-d", help="Human-readable name"
55
+ ),
56
+ description: Optional[str] = typer.Option(
57
+ None, "--description", help="Short description of the agent"
58
+ ),
59
+ category: Optional[str] = typer.Option(
60
+ None, "--category", "-c", help="Agent category for marketplace"
61
+ ),
62
+ template: Optional[str] = typer.Option(
63
+ None, "--template", "-t", help="Project template (basic, with-tools, with-views, full)"
64
+ ),
65
+ directory: Optional[str] = typer.Option(
66
+ None, "--directory", help="Directory to create agent in (defaults to current directory)"
67
+ ),
68
+ non_interactive: bool = typer.Option(
69
+ False, "--yes", "-y", help="Skip interactive prompts (requires all options)"
70
+ ),
71
+ ) -> None:
72
+ """
73
+ Initialize a new A4E agent project.
74
+
75
+ Run without arguments for an interactive wizard, or pass all options for non-interactive mode.
76
+ """
77
+ console.print(Panel.fit(
78
+ "[bold blue]A4E Agent Creator[/bold blue]\n"
79
+ "Create a new conversational AI agent",
80
+ border_style="blue"
81
+ ))
82
+
83
+ # Interactive mode - prompt for missing values
84
+ if not non_interactive:
85
+ # Agent name
86
+ if not name:
87
+ while True:
88
+ name = Prompt.ask(
89
+ "\n[bold]Agent name[/bold] (lowercase, hyphens allowed)",
90
+ default="my-agent"
91
+ )
92
+ if validate_agent_name(name):
93
+ break
94
+ console.print("[red]Invalid name. Use lowercase letters, numbers, and hyphens only.[/red]")
95
+
96
+ # Display name
97
+ if not display_name:
98
+ default_display = name.replace("-", " ").replace("_", " ").title()
99
+ display_name = Prompt.ask(
100
+ "[bold]Display name[/bold]",
101
+ default=default_display
102
+ )
103
+
104
+ # Description
105
+ if not description:
106
+ description = Prompt.ask(
107
+ "[bold]Description[/bold]",
108
+ default=f"A helpful {display_name} agent"
109
+ )
110
+
111
+ # Category selection
112
+ if not category:
113
+ console.print("\n[bold]Select a category:[/bold]")
114
+ for i, cat in enumerate(CATEGORIES, 1):
115
+ console.print(f" [{i}] {cat}")
116
+
117
+ while True:
118
+ choice = Prompt.ask("Enter number", default="9")
119
+ try:
120
+ idx = int(choice) - 1
121
+ if 0 <= idx < len(CATEGORIES):
122
+ category = CATEGORIES[idx]
123
+ break
124
+ except ValueError:
125
+ pass
126
+ console.print("[red]Invalid selection. Please enter a number.[/red]")
127
+
128
+ # Template selection
129
+ if not template:
130
+ console.print("\n[bold]Select a template:[/bold]")
131
+ for i, (key, desc) in enumerate(TEMPLATES.items(), 1):
132
+ console.print(f" [{i}] {key}: {desc}")
133
+
134
+ while True:
135
+ choice = Prompt.ask("Enter number", default="1")
136
+ try:
137
+ idx = int(choice) - 1
138
+ if 0 <= idx < len(TEMPLATES):
139
+ template = list(TEMPLATES.keys())[idx]
140
+ break
141
+ except ValueError:
142
+ pass
143
+ console.print("[red]Invalid selection. Please enter a number.[/red]")
144
+
145
+ # Validate all required fields
146
+ if not all([name, display_name, description, category, template]):
147
+ console.print("[red]Error: All fields are required. Use --yes for non-interactive mode only with all options.[/red]")
148
+ raise typer.Exit(code=1)
149
+
150
+ if not validate_agent_name(name):
151
+ console.print("[red]Error: Agent name must be lowercase with hyphens/underscores only.[/red]")
152
+ raise typer.Exit(code=1)
153
+
154
+ if category not in CATEGORIES:
155
+ console.print(f"[red]Error: Invalid category. Choose from: {', '.join(CATEGORIES)}[/red]")
156
+ raise typer.Exit(code=1)
157
+
158
+ if template not in TEMPLATES:
159
+ console.print(f"[red]Error: Invalid template. Choose from: {', '.join(TEMPLATES.keys())}[/red]")
160
+ raise typer.Exit(code=1)
161
+
162
+ # Confirmation
163
+ if not non_interactive:
164
+ console.print("\n[bold]Creating agent with:[/bold]")
165
+ console.print(f" Name: {name}")
166
+ console.print(f" Display Name: {display_name}")
167
+ console.print(f" Description: {description}")
168
+ console.print(f" Category: {category}")
169
+ console.print(f" Template: {template}")
170
+
171
+ if not Confirm.ask("\nProceed?", default=True):
172
+ console.print("[yellow]Cancelled.[/yellow]")
173
+ raise typer.Exit(code=0)
174
+
175
+ # Import and call the MCP tool function directly
176
+ try:
177
+ from ..tools.project.initialize_project import initialize_project
178
+
179
+ # Set environment variable for directory if specified
180
+ import os
181
+ if directory:
182
+ os.environ["A4E_WORKSPACE"] = str(Path(directory).resolve())
183
+
184
+ result = initialize_project(
185
+ name=name,
186
+ display_name=display_name,
187
+ description=description,
188
+ category=category,
189
+ template=template,
190
+ )
191
+
192
+ if result.get("success"):
193
+ console.print(f"\n[green]✓ Agent '{name}' created successfully![/green]")
194
+ console.print(f" Path: {result.get('path')}")
195
+
196
+ console.print("\n[bold]Next steps:[/bold]")
197
+ for step in result.get("next_steps", []):
198
+ console.print(f" • {step}")
199
+
200
+ console.print(f"\n cd {name}")
201
+ console.print(" a4e dev start")
202
+ else:
203
+ console.print(f"\n[red]Error: {result.get('error')}[/red]")
204
+ raise typer.Exit(code=1)
205
+
206
+ except ImportError as e:
207
+ console.print(f"[red]Error importing tools: {e}[/red]")
208
+ raise typer.Exit(code=1)
209
+ except Exception as e:
210
+ console.print(f"[red]Error: {e}[/red]")
211
+ raise typer.Exit(code=1)