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,60 @@
1
+ """
2
+ {{ tool_name | replace('_', ' ') | title }} Tool
3
+
4
+ {{ description }}
5
+ """
6
+ from typing import Dict, Any, Optional
7
+ import sys
8
+ import os
9
+
10
+
11
+ def _ensure_imports():
12
+ """
13
+ Add tools directory to sys.path if not already there.
14
+ This enables imports of support files (db.py, models.py, etc.) when exec()'d.
15
+ """
16
+ if '__file__' in globals():
17
+ tools_dir = os.path.dirname(globals()['__file__'])
18
+ if tools_dir not in sys.path:
19
+ sys.path.insert(0, tools_dir)
20
+
21
+
22
+ def {{ tool_name }}(params: Dict[str, Any]) -> Dict[str, Any]:
23
+ """
24
+ {{ description }}
25
+
26
+ Args:
27
+ params: Dictionary containing:
28
+ {% for param_name, param_info in parameters.items() %}
29
+ - {{ param_name }}: {{ param_info.description if param_info.description else param_info.type }}{% if not param_info.is_required %} (optional){% endif %}
30
+
31
+ {% endfor %}
32
+ Returns:
33
+ Dict with operation result
34
+ """
35
+ # Extract parameters from params dict
36
+ {% for param_name, param_info in parameters.items() %}
37
+ {% if param_info.is_required %}
38
+ {{ param_name }} = params.get("{{ param_name }}")
39
+ {% else %}
40
+ {{ param_name }} = params.get("{{ param_name }}")
41
+ {% endif %}
42
+ {% endfor %}
43
+
44
+ # Ensure imports work in exec() context
45
+ _ensure_imports()
46
+
47
+ # TODO: Implement {{ tool_name }} logic here
48
+ # Example: Import support modules if needed
49
+ # try:
50
+ # import db
51
+ # except ImportError:
52
+ # return {"status": "error", "message": "Database module not available"}
53
+
54
+ return {
55
+ "status": "success",
56
+ "message": "{{ tool_name | replace('_', ' ') | title }} executed successfully",
57
+ {% for param_name, param_info in parameters.items() %}
58
+ "{{ param_name }}": {{ param_name }},
59
+ {% endfor %}
60
+ }
@@ -0,0 +1,192 @@
1
+ # AGENTS.md — Tools Directory
2
+
3
+ > This file provides context and instructions for AI coding agents working on tools in this A4E agent project.
4
+
5
+ ## Overview
6
+
7
+ This `tools/` directory contains Python functions decorated with `@tool` that give the agent the ability to perform actions or retrieve information. Each tool is a self-contained Python file that gets automatically discovered and registered.
8
+
9
+ ## Directory Structure
10
+
11
+ ```
12
+ tools/
13
+ ├── AGENTS.md # This file
14
+ ├── schemas.json # Auto-generated schemas (do not edit manually)
15
+ ├── example_tool.py # Example tool (if template includes it)
16
+ └── <your_tools>.py # Your custom tools
17
+ ```
18
+
19
+ ## Code Style
20
+
21
+ - Python 3.11+ required
22
+ - Use type hints for ALL function parameters and return types
23
+ - Use `Optional[T]` for optional parameters, not `T | None`
24
+ - Snake_case for function and file names
25
+ - Each tool must return a `dict` with a clear response structure
26
+
27
+ ## Tool Structure
28
+
29
+ Every tool file must follow this pattern:
30
+
31
+ ```python
32
+ from a4e.sdk import tool
33
+ from typing import Optional, List, Any
34
+
35
+ @tool
36
+ def tool_name(
37
+ required_param: str,
38
+ optional_param: Optional[int] = None
39
+ ) -> dict:
40
+ """
41
+ Brief description of what the tool does.
42
+
43
+ Args:
44
+ required_param: Description of this parameter
45
+ optional_param: Description of this optional parameter
46
+ """
47
+ # Implementation here
48
+ return {
49
+ "status": "success",
50
+ "message": "Result message",
51
+ "data": {} # Optional: include relevant data
52
+ }
53
+ ```
54
+
55
+ ## Type Mapping
56
+
57
+ When creating tools, use these Python types:
58
+
59
+ | Use Case | Type | Example |
60
+ | ----------------- | ------------------------ | ------------------------------ |
61
+ | Text | `str` | `name: str` |
62
+ | Integer | `int` | `count: int` |
63
+ | Decimal | `float` | `price: float` |
64
+ | Boolean | `bool` | `active: bool` |
65
+ | List | `List[T]` | `items: List[str]` |
66
+ | Dictionary | `dict` | `config: dict` |
67
+ | Optional | `Optional[T]` | `limit: Optional[int] = None` |
68
+ | Literal choices | `Literal["a", "b"]` | `mode: Literal["fast", "slow"]`|
69
+ | Any type | `Any` | `data: Any` |
70
+
71
+ ## Return Value Conventions
72
+
73
+ Always return a dictionary with consistent keys:
74
+
75
+ ```python
76
+ # Success response
77
+ return {
78
+ "status": "success",
79
+ "message": "Human-readable success message",
80
+ "data": {...} # Tool-specific data
81
+ }
82
+
83
+ # Error response
84
+ return {
85
+ "status": "error",
86
+ "message": "Human-readable error message",
87
+ "error_code": "SPECIFIC_ERROR" # Optional: machine-readable code
88
+ }
89
+ ```
90
+
91
+ ## Naming Conventions
92
+
93
+ - **File names**: `snake_case.py` (e.g., `calculate_bmi.py`, `fetch_weather.py`)
94
+ - **Function names**: Match file name exactly (e.g., `def calculate_bmi(...)`)
95
+ - **Parameter names**: Descriptive snake_case (e.g., `user_weight_kg`, `max_results`)
96
+
97
+ ## Adding a New Tool
98
+
99
+ 1. Create a new file: `tools/<tool_name>.py`
100
+ 2. Use the `@tool` decorator from `a4e.sdk`
101
+ 3. Add comprehensive docstring with Args section
102
+ 4. Implement the logic returning a dict
103
+ 5. Run schema generation to update `schemas.json`
104
+
105
+ ## Schema Generation
106
+
107
+ Schemas are auto-generated from your tool code. After adding or modifying tools:
108
+
109
+ - Schemas are regenerated automatically when using MCP tools
110
+ - The `schemas.json` file is auto-generated — **do not edit manually**
111
+ - Docstrings become the tool's description in the schema
112
+
113
+ ## Testing Your Tools
114
+
115
+ Before deployment, verify your tools:
116
+
117
+ 1. Check syntax: `python -m py_compile tools/<tool_name>.py`
118
+ 2. Import test: `python -c "from tools.<tool_name> import <tool_name>; print('OK')"`
119
+ 3. Run via dev server to test integration
120
+
121
+ ## Common Patterns
122
+
123
+ ### API Calls
124
+
125
+ ```python
126
+ import requests
127
+
128
+ @tool
129
+ def fetch_data(endpoint: str) -> dict:
130
+ """Fetch data from an API endpoint."""
131
+ try:
132
+ response = requests.get(endpoint, timeout=10)
133
+ response.raise_for_status()
134
+ return {"status": "success", "data": response.json()}
135
+ except Exception as e:
136
+ return {"status": "error", "message": str(e)}
137
+ ```
138
+
139
+ ### Calculations
140
+
141
+ ```python
142
+ @tool
143
+ def calculate_bmi(weight_kg: float, height_m: float) -> dict:
144
+ """Calculate Body Mass Index."""
145
+ if height_m <= 0:
146
+ return {"status": "error", "message": "Height must be positive"}
147
+
148
+ bmi = weight_kg / (height_m ** 2)
149
+ return {
150
+ "status": "success",
151
+ "bmi": round(bmi, 2),
152
+ "category": "normal" if 18.5 <= bmi < 25 else "other"
153
+ }
154
+ ```
155
+
156
+ ### Data Transformation
157
+
158
+ ```python
159
+ from typing import List
160
+
161
+ @tool
162
+ def format_list(items: List[str], separator: str = ", ") -> dict:
163
+ """Format a list of items into a string."""
164
+ return {
165
+ "status": "success",
166
+ "result": separator.join(items),
167
+ "count": len(items)
168
+ }
169
+ ```
170
+
171
+ ## Security Considerations
172
+
173
+ - Never hardcode sensitive data (API keys, passwords)
174
+ - Validate and sanitize all user inputs
175
+ - Use try/except for external calls
176
+ - Limit resource usage (timeouts, max sizes)
177
+
178
+ ## Troubleshooting
179
+
180
+ ### Tool not appearing in schema
181
+
182
+ - Verify the `@tool` decorator is present
183
+ - Check for syntax errors in the file
184
+ - Ensure the function has type hints and docstring
185
+ - Run schema generation manually
186
+
187
+ ### Import errors
188
+
189
+ - Check all imports are available in the environment
190
+ - Use absolute imports when possible
191
+ - Verify dependency is in the project requirements
192
+
@@ -0,0 +1,21 @@
1
+ "use client";
2
+ import React from "react";
3
+ import { a4e } from "@/lib/sdk";
4
+
5
+ interface {{ view_name }}Props {
6
+ {% for prop_name, prop_info in props.items() %}
7
+ {{ prop_name }}: {{ prop_info.type }};
8
+ {% endfor %}
9
+ }
10
+
11
+ export default function {{ view_name }}View(props: {{ view_name }}Props) {
12
+ const { {% for prop_name in props.keys() %}{{ prop_name }}{% if not loop.last %}, {% endif %}{% endfor %} } = props;
13
+
14
+ return (
15
+ <div className="p-6">
16
+ <h2>{{ description }}</h2>
17
+ {/* View content */}
18
+ <pre>{JSON.stringify(props, null, 2)}</pre>
19
+ </div>
20
+ );
21
+ }
@@ -0,0 +1,219 @@
1
+ # AGENTS.md — Views Directory
2
+
3
+ > This file provides context and instructions for AI coding agents working on views in this A4E agent project.
4
+
5
+ ## Overview
6
+
7
+ This `views/` directory contains React components that provide rich graphical interfaces for the agent's responses. Each view is a self-contained directory with a `view.tsx` file.
8
+
9
+ ## Directory Structure
10
+
11
+ ```
12
+ views/
13
+ ├── AGENTS.md # This file
14
+ ├── schemas.json # Auto-generated schemas (do not edit manually)
15
+ ├── welcome/ # Mandatory welcome view
16
+ │ └── view.tsx
17
+ └── <view_id>/ # Your custom views
18
+ └── view.tsx
19
+ ```
20
+
21
+ ## Code Style
22
+
23
+ - TypeScript with React
24
+ - Use functional components with hooks
25
+ - camelCase for variables and functions
26
+ - PascalCase for component names
27
+ - Props interface must be explicitly defined
28
+
29
+ ## View Structure
30
+
31
+ Every view file must follow this pattern:
32
+
33
+ ```tsx
34
+ "use client";
35
+ import React from "react";
36
+ import { a4e } from "@/lib/sdk";
37
+
38
+ interface ViewNameProps {
39
+ title: string;
40
+ count: number;
41
+ items?: string[];
42
+ }
43
+
44
+ export default function ViewNameView(props: ViewNameProps) {
45
+ const { title, count, items = [] } = props;
46
+
47
+ return (
48
+ <div className="p-6">
49
+ <h2 className="text-xl font-bold">{title}</h2>
50
+ <p>Count: {count}</p>
51
+ {items.length > 0 && (
52
+ <ul>
53
+ {items.map((item, i) => (
54
+ <li key={i}>{item}</li>
55
+ ))}
56
+ </ul>
57
+ )}
58
+ </div>
59
+ );
60
+ }
61
+ ```
62
+
63
+ ## Naming Conventions
64
+
65
+ - **Directory names**: `snake_case` (e.g., `meal_plan`, `bmi_result`)
66
+ - **Component names**: `PascalCase + View` (e.g., `MealPlanView`, `BmiResultView`)
67
+ - **Prop names**: `camelCase` (e.g., `userName`, `maxItems`)
68
+
69
+ ## Type Mapping
70
+
71
+ When defining props, use TypeScript types:
72
+
73
+ | Use Case | Type | Example |
74
+ | ----------------- | ------------------ | --------------------------- |
75
+ | Text | `string` | `title: string` |
76
+ | Integer/Decimal | `number` | `count: number` |
77
+ | Boolean | `boolean` | `isActive: boolean` |
78
+ | Array | `T[]` | `items: string[]` |
79
+ | Object | `object` | `user: object` |
80
+ | Optional | `T?` or `T \| undefined` | `subtitle?: string` |
81
+ | Union | `"a" \| "b"` | `status: "active" \| "inactive"` |
82
+
83
+ ## Adding a New View
84
+
85
+ 1. Create directory: `views/<view_id>/`
86
+ 2. Create file: `views/<view_id>/view.tsx`
87
+ 3. Define props interface at the top
88
+ 4. Export default functional component
89
+ 5. Run schema generation to update `schemas.json`
90
+
91
+ ## Schema Generation
92
+
93
+ Schemas are auto-generated from your TypeScript interfaces:
94
+
95
+ - Props interface becomes the view's input schema
96
+ - Type annotations are converted to JSON Schema types
97
+ - Comments become descriptions in the schema
98
+ - The `schemas.json` file is auto-generated — **do not edit manually**
99
+
100
+ ## Styling Guidelines
101
+
102
+ Use Tailwind CSS classes for styling:
103
+
104
+ ```tsx
105
+ // Good: Tailwind utilities
106
+ <div className="p-4 bg-white rounded-lg shadow-md">
107
+ <h2 className="text-lg font-semibold text-gray-900">Title</h2>
108
+ </div>
109
+
110
+ // Prefer Tailwind over inline styles
111
+ <div className="p-4">Content</div>
112
+ ```
113
+
114
+ ## Common Patterns
115
+
116
+ ### Card Layout
117
+
118
+ ```tsx
119
+ export default function CardView(props: { title: string; content: string }) {
120
+ return (
121
+ <div className="bg-white rounded-xl shadow-lg p-6 max-w-md">
122
+ <h3 className="text-xl font-bold mb-4">{props.title}</h3>
123
+ <p className="text-gray-600">{props.content}</p>
124
+ </div>
125
+ );
126
+ }
127
+ ```
128
+
129
+ ### List Display
130
+
131
+ ```tsx
132
+ interface ListViewProps {
133
+ title: string;
134
+ items: Array<{ id: string; name: string }>;
135
+ }
136
+
137
+ export default function ListView({ title, items }: ListViewProps) {
138
+ return (
139
+ <div className="space-y-4">
140
+ <h2 className="text-2xl font-bold">{title}</h2>
141
+ <ul className="divide-y divide-gray-200">
142
+ {items.map((item) => (
143
+ <li key={item.id} className="py-3">
144
+ {item.name}
145
+ </li>
146
+ ))}
147
+ </ul>
148
+ </div>
149
+ );
150
+ }
151
+ ```
152
+
153
+ ### Data Visualization
154
+
155
+ ```tsx
156
+ interface ChartViewProps {
157
+ data: Array<{ label: string; value: number }>;
158
+ }
159
+
160
+ export default function ChartView({ data }: ChartViewProps) {
161
+ return (
162
+ <div className="space-y-2">
163
+ {data.map((item, i) => (
164
+ <div key={i} className="flex items-center gap-4">
165
+ <span className="w-24 text-sm">{item.label}</span>
166
+ <div className="h-6 bg-blue-500 rounded w-full" />
167
+ <span className="text-sm font-medium">{item.value}</span>
168
+ </div>
169
+ ))}
170
+ </div>
171
+ );
172
+ }
173
+ ```
174
+
175
+ ## A4E SDK Integration
176
+
177
+ Use the A4E SDK for agent interactions:
178
+
179
+ ```tsx
180
+ import { a4e } from "@/lib/sdk";
181
+
182
+ export default function InteractiveView(props: { query: string }) {
183
+ const handleAction = async () => {
184
+ // Trigger agent action
185
+ await a4e.triggerAction("perform_search", { query: props.query });
186
+ };
187
+
188
+ return (
189
+ <button onClick={handleAction} className="btn-primary">
190
+ Search
191
+ </button>
192
+ );
193
+ }
194
+ ```
195
+
196
+ ## Accessibility
197
+
198
+ - Use semantic HTML elements (`<main>`, `<nav>`, `<article>`)
199
+ - Add `alt` text to images
200
+ - Ensure sufficient color contrast
201
+ - Support keyboard navigation
202
+ - Use ARIA labels where needed
203
+
204
+ ## Troubleshooting
205
+
206
+ ### View not appearing in schema
207
+
208
+ - Verify the props interface is exported or defined at module level
209
+ - Check for TypeScript syntax errors
210
+ - Ensure the component is the default export
211
+ - Run schema generation manually
212
+
213
+ ### Styling not applied
214
+
215
+ - Verify Tailwind classes are valid
216
+ - Check for typos in class names
217
+ - Ensure the build process includes Tailwind
218
+ - Use browser dev tools to inspect applied styles
219
+
a4e/tools/__init__.py ADDED
@@ -0,0 +1,70 @@
1
+ """
2
+ A4E MCP Tools - All tools for agent creation and management.
3
+
4
+ Structure:
5
+ - project/ : Project initialization (initialize_project, get_agent_info)
6
+ - agent_tools/ : Tool management (add_tool, list_tools, remove_tool, update_tool)
7
+ - views/ : View management (add_view, list_views, remove_view, update_view)
8
+ - skills/ : Skill management (add_skill, list_skills, remove_skill, update_skill)
9
+ - schemas/ : Schema generation (generate_schemas)
10
+ - validation/ : Validation (validate)
11
+ - dev/ : Development server (dev_start, dev_stop, check_environment)
12
+ - deploy/ : Deployment (deploy)
13
+ """
14
+
15
+ # Project tools
16
+ from .project import initialize_project, get_agent_info, get_instructions
17
+
18
+ # Agent tools management
19
+ from .agent_tools import add_tool, list_tools, remove_tool, update_tool
20
+
21
+ # Views management
22
+ from .views import add_view, list_views, remove_view, update_view
23
+
24
+ # Skills management
25
+ from .skills import add_skill, list_skills, remove_skill, update_skill
26
+
27
+ # Schema generation
28
+ from .schemas import generate_schemas
29
+
30
+ # Validation
31
+ from .validation import validate
32
+
33
+ # Development
34
+ from .dev import dev_start, dev_stop, check_environment
35
+
36
+ # Deployment
37
+ from .deploy import deploy
38
+
39
+ __all__ = [
40
+ # Project
41
+ "initialize_project",
42
+ "get_agent_info",
43
+ "get_instructions",
44
+ # Agent tools
45
+ "add_tool",
46
+ "list_tools",
47
+ "remove_tool",
48
+ "update_tool",
49
+ # Views
50
+ "add_view",
51
+ "list_views",
52
+ "remove_view",
53
+ "update_view",
54
+ # Skills
55
+ "add_skill",
56
+ "list_skills",
57
+ "remove_skill",
58
+ "update_skill",
59
+ # Schemas
60
+ "generate_schemas",
61
+ # Validation
62
+ "validate",
63
+ # Development
64
+ "dev_start",
65
+ "dev_stop",
66
+ "check_environment",
67
+ # Deployment
68
+ "deploy",
69
+ ]
70
+
@@ -0,0 +1,12 @@
1
+ """
2
+ Agent tools management.
3
+ """
4
+
5
+ from .add_tool import add_tool
6
+ from .add_support_module import add_support_module
7
+ from .list_tools import list_tools
8
+ from .remove_tool import remove_tool
9
+ from .update_tool import update_tool
10
+
11
+ __all__ = ["add_tool", "add_support_module", "list_tools", "remove_tool", "update_tool"]
12
+
@@ -0,0 +1,95 @@
1
+ """
2
+ Add support module tool.
3
+
4
+ Creates support modules (db.py, models.py, etc.) with exec() compatibility
5
+ for proper loading in the A4E main application.
6
+ """
7
+
8
+ from typing import Optional
9
+
10
+ from ...core import mcp, jinja_env, get_project_dir
11
+
12
+
13
+ @mcp.tool()
14
+ def add_support_module(
15
+ module_name: str,
16
+ description: str,
17
+ agent_name: Optional[str] = None
18
+ ) -> dict:
19
+ """
20
+ Add a support module (db.py, models.py, etc.) with exec() compatibility
21
+
22
+ Args:
23
+ module_name: Name of the module (snake_case, e.g., "db", "models", "helpers")
24
+ description: What the module does
25
+ agent_name: Optional agent ID if not in agent directory
26
+
27
+ Support modules are loaded by all tools and need special handling
28
+ to work in the exec() context used by the A4E main application.
29
+
30
+ The generated module includes:
31
+ - __name__ definition for exec() compatibility
32
+ - sys.path manipulation for imports
33
+ - Self-test code that only runs when executed directly
34
+ """
35
+ # Validate module name
36
+ if not module_name.replace("_", "").isalnum():
37
+ suggested = module_name.replace("-", "_").replace(" ", "_").lower()
38
+ return {
39
+ "success": False,
40
+ "error": "Module name must be alphanumeric with underscores",
41
+ "fix": f"Try: {suggested}",
42
+ }
43
+
44
+ project_dir = get_project_dir(agent_name)
45
+ tools_dir = project_dir / "tools"
46
+
47
+ if not tools_dir.exists():
48
+ return {
49
+ "success": False,
50
+ "error": f"tools/ directory not found at {tools_dir}",
51
+ "fix": "Initialize an agent first with initialize_project() or specify agent_name",
52
+ }
53
+
54
+ module_file = tools_dir / f"{module_name}.py"
55
+ if module_file.exists():
56
+ return {
57
+ "success": False,
58
+ "error": f"Module '{module_name}' already exists",
59
+ "fix": "Edit the existing file or remove it first",
60
+ }
61
+
62
+ try:
63
+ # Get agent name for template
64
+ metadata_file = project_dir / "metadata.json"
65
+ agent_display_name = module_name
66
+ if metadata_file.exists():
67
+ import json
68
+ try:
69
+ metadata = json.loads(metadata_file.read_text())
70
+ agent_display_name = metadata.get("name", module_name)
71
+ except Exception:
72
+ pass
73
+
74
+ template = jinja_env.get_template("support_module.py.j2")
75
+ code = template.render(
76
+ module_name=module_name,
77
+ description=description,
78
+ agent_name=agent_display_name
79
+ )
80
+ module_file.write_text(code)
81
+
82
+ return {
83
+ "success": True,
84
+ "message": f"Created support module '{module_name}' with exec() compatibility",
85
+ "path": str(module_file),
86
+ "notes": [
87
+ "Module includes __name__ fix for exec() context",
88
+ "Module includes sys.path manipulation for imports",
89
+ "Self-test code only runs when executed directly",
90
+ "This module will NOT be included in tools/schemas.json"
91
+ ]
92
+ }
93
+ except Exception as e:
94
+ return {"success": False, "error": str(e)}
95
+