multiagent-init 0.1.0__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.
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"
multiagent_init/cli.py ADDED
@@ -0,0 +1,38 @@
1
+ from __future__ import annotations
2
+
3
+ import sys
4
+
5
+ from .generator import generate_project
6
+ from .wizard import run_wizard
7
+
8
+
9
+ def main() -> int:
10
+ try:
11
+ config = run_wizard()
12
+ destination = generate_project(config)
13
+
14
+ print()
15
+ print("🎉 Done!")
16
+ print()
17
+ print("Next steps:")
18
+ print()
19
+ print(f" cd {destination.name}")
20
+ print(" python3 -m venv .venv")
21
+ print(" source .venv/bin/activate # macOS/Linux")
22
+ print(" pip install .")
23
+ print(" python main.py")
24
+ print()
25
+
26
+ return 0
27
+
28
+ except KeyboardInterrupt:
29
+ print("\n\nCancelled.")
30
+ return 130
31
+
32
+ except (ValueError, FileExistsError, OSError) as exc:
33
+ print(f"\nError: {exc}", file=sys.stderr)
34
+ return 1
35
+
36
+
37
+ if __name__ == "__main__":
38
+ raise SystemExit(main())
@@ -0,0 +1,315 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from pathlib import Path
5
+
6
+ from .templates_data import (
7
+ AGENT_TEMPLATES,
8
+ CONFIG_TEMPLATE,
9
+ DOCKER_FILE,
10
+ FRAMEWORK_FILES,
11
+ ORCHESTRATOR_TEMPLATES,
12
+ PROVIDER_ENV_VARS,
13
+ TEST_TEMPLATE,
14
+ TOOL_FILE,
15
+ )
16
+
17
+ DEFAULT_AGENT_CLASSES = {
18
+ "Researcher": "ResearcherAgent",
19
+ "Writer": "WriterAgent",
20
+ "Reviewer": "ReviewerAgent",
21
+ "Planner": "PlannerAgent",
22
+ "Executor": "ExecutorAgent",
23
+ }
24
+
25
+
26
+ def _safe_project_name(value: str) -> str:
27
+ value = value.strip()
28
+
29
+ if not value:
30
+ raise ValueError("project name cannot be empty")
31
+
32
+ if not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9_-]*", value):
33
+ raise ValueError("invalid project name")
34
+
35
+ return value
36
+
37
+
38
+ def _safe_package_name(project_name: str) -> str:
39
+ value = re.sub(r"[^A-Za-z0-9_]", "_", project_name).lower()
40
+
41
+ if value[0].isdigit():
42
+ value = f"_{value}"
43
+
44
+ return value
45
+
46
+
47
+ def agent_to_module(name: str) -> str:
48
+ value = re.sub(r"[^A-Za-z0-9]+", "_", name.strip())
49
+ value = re.sub(r"_+", "_", value).strip("_").lower()
50
+
51
+ if not value:
52
+ value = "agent"
53
+
54
+ if value[0].isdigit():
55
+ value = f"agent_{value}"
56
+
57
+ return value
58
+
59
+
60
+ def agent_to_class(name: str) -> str:
61
+ parts = re.findall(r"[A-Za-z0-9]+", name.strip())
62
+
63
+ if not parts:
64
+ return "Agent"
65
+
66
+ class_name = "".join(part.capitalize() for part in parts)
67
+
68
+ if class_name[0].isdigit():
69
+ class_name = f"Agent{class_name}"
70
+
71
+ return f"{class_name}Agent"
72
+
73
+
74
+ def _write(path: Path, content: str) -> None:
75
+ path.parent.mkdir(parents=True, exist_ok=True)
76
+ path.write_text(content, encoding="utf-8")
77
+
78
+
79
+ def generate_project(config: dict) -> Path:
80
+ project_name = _safe_project_name(config["project_name"])
81
+ package_name = _safe_package_name(project_name)
82
+ destination = Path.cwd() / project_name
83
+
84
+ if destination.exists():
85
+ if not destination.is_dir():
86
+ raise FileExistsError(f"'{project_name}' already exists and is not a directory")
87
+
88
+ if any(destination.iterdir()):
89
+ raise FileExistsError(
90
+ f"directory '{project_name}' already exists and is not empty"
91
+ )
92
+ else:
93
+ destination.mkdir(parents=True)
94
+
95
+ package_dir = destination / "src" / package_name
96
+
97
+ framework_pyproject = FRAMEWORK_FILES[config["framework"]]
98
+ framework_pyproject = framework_pyproject.replace("{{PROJECT_NAME}}", project_name)
99
+ framework_pyproject = framework_pyproject.replace("{{PACKAGE_NAME}}", package_name)
100
+
101
+ _write(destination / "pyproject.toml", framework_pyproject)
102
+
103
+ _write(
104
+ destination / "README.md",
105
+ _render_readme(config, package_name),
106
+ )
107
+
108
+ provider_env_var = PROVIDER_ENV_VARS[config["provider"]]
109
+
110
+ _write(
111
+ destination / ".env",
112
+ f"MODEL_NAME=your-model\n{provider_env_var}=\n",
113
+ )
114
+
115
+ _write(
116
+ destination / ".gitignore",
117
+ "__pycache__/\n*.py[cod]\n.venv/\n.env\n.pytest_cache/\n",
118
+ )
119
+
120
+ _write(package_dir / "__init__.py", '"""Generated package."""\n')
121
+
122
+ config_content = CONFIG_TEMPLATE.replace("{{PROVIDER_ENV_VAR}}", provider_env_var)
123
+ config_content = config_content.replace("{{PROVIDER}}", config["provider"])
124
+ _write(package_dir / "config.py", config_content)
125
+
126
+ agent_template = AGENT_TEMPLATES.get(config["framework"], AGENT_TEMPLATES["basic"])
127
+ orchestrator_template = ORCHESTRATOR_TEMPLATES.get(
128
+ config["framework"], ORCHESTRATOR_TEMPLATES["basic"]
129
+ )
130
+
131
+ agent_files = []
132
+
133
+ for agent_name in config["agent_names"]:
134
+ module_name = agent_to_module(agent_name)
135
+ class_name = agent_to_class(agent_name)
136
+
137
+ agent_files.append(
138
+ {
139
+ "name": agent_name,
140
+ "module": module_name,
141
+ "class": class_name,
142
+ }
143
+ )
144
+
145
+ content = agent_template
146
+ content = content.replace("{{AGENT_NAME}}", agent_name)
147
+ content = content.replace("{{AGENT_CLASS}}", class_name)
148
+
149
+ _write(
150
+ package_dir / "agents" / f"{module_name}.py",
151
+ content,
152
+ )
153
+
154
+ _write(
155
+ package_dir / "agents" / "__init__.py",
156
+ '"""Generated agents."""\n',
157
+ )
158
+
159
+ imports = "\n".join(
160
+ f"from {package_name}.agents.{item['module']} import {item['class']}"
161
+ for item in agent_files
162
+ )
163
+
164
+ instances = ",\n ".join(
165
+ f"{item['class']}()"
166
+ for item in agent_files
167
+ )
168
+
169
+ main_content = f"""from __future__ import annotations
170
+
171
+ import logging
172
+
173
+ {imports}
174
+
175
+ from {package_name} import config
176
+ from {package_name}.workflow.orchestrator import Orchestrator
177
+
178
+
179
+ def main() -> None:
180
+ logging.basicConfig(
181
+ level=logging.INFO,
182
+ format="%(asctime)s %(levelname)s %(name)s: %(message)s",
183
+ )
184
+
185
+ config.validate()
186
+
187
+ agents = [
188
+ {instances}
189
+ ]
190
+
191
+ orchestrator = Orchestrator(agents)
192
+
193
+ result = orchestrator.run(
194
+ "Build a short report about multi-agent systems."
195
+ )
196
+
197
+ print("\\n=== Final result ===")
198
+ print(result)
199
+
200
+
201
+ if __name__ == "__main__":
202
+ main()
203
+ """
204
+
205
+ _write(destination / "main.py", main_content)
206
+
207
+ _write(
208
+ package_dir / "workflow" / "__init__.py",
209
+ '"""Workflow and orchestration."""\n',
210
+ )
211
+
212
+ _write(
213
+ package_dir / "workflow" / "orchestrator.py",
214
+ orchestrator_template,
215
+ )
216
+
217
+ if config["include_tools"]:
218
+ _write(
219
+ package_dir / "tools" / "__init__.py",
220
+ '"""Tools available to agents."""\n',
221
+ )
222
+ _write(package_dir / "tools" / "web_search.py", TOOL_FILE)
223
+
224
+ if config["include_tests"]:
225
+ test_content = TEST_TEMPLATE.replace(
226
+ "{{TEST_IMPORTS}}",
227
+ "\n".join(
228
+ f"from {package_name}.agents.{item['module']} import {item['class']}"
229
+ for item in agent_files
230
+ ),
231
+ )
232
+
233
+ assertions = "\n".join(
234
+ f' self.assertIn("{item["name"]}", {item["class"]}().run(task))'
235
+ for item in agent_files
236
+ )
237
+
238
+ test_content = test_content.replace("{{ASSERTIONS}}", assertions)
239
+
240
+ _write(
241
+ destination / "tests" / "test_agents.py",
242
+ test_content,
243
+ )
244
+
245
+ if config["include_docker"]:
246
+ docker_content = DOCKER_FILE.replace("{{PACKAGE_NAME}}", package_name)
247
+ _write(destination / "Dockerfile", docker_content)
248
+
249
+ print()
250
+ print("Creating project...")
251
+ print()
252
+ print("✓ Agents")
253
+ print("✓ Workflow")
254
+
255
+ if config["include_tools"]:
256
+ print("✓ Tools")
257
+
258
+ print("✓ Configuration")
259
+
260
+ if config["include_tests"]:
261
+ print("✓ Tests")
262
+
263
+ if config["include_docker"]:
264
+ print("✓ Docker")
265
+
266
+ print("✓ README")
267
+
268
+ return destination
269
+
270
+
271
+ def _render_readme(config: dict, package_name: str) -> str:
272
+ agents = "\n".join(
273
+ f"- `{name}`"
274
+ for name in config["agent_names"]
275
+ )
276
+
277
+ return f"""# {config["project_name"]}
278
+
279
+ Generated by **multiagent-init**.
280
+
281
+ ## Configuration
282
+
283
+ - Framework: `{config["framework"]}`
284
+ - Model provider: `{config["provider"]}`
285
+ - Agent configuration: `{config["agent_mode"]}`
286
+ - Number of agents: `{config["agent_count"]}`
287
+
288
+ ## Agents
289
+
290
+ {agents}
291
+
292
+ ## Structure
293
+
294
+ ```text
295
+ src/{package_name}/
296
+ agents/ Individual agents
297
+ workflow/ Orchestration
298
+ tools/ Optional tools
299
+ config.py Configuration
300
+ tests/ Optional tests
301
+ main.py Entry point (python main.py)
302
+ ```
303
+
304
+ ## Run
305
+
306
+ ```bash
307
+ pip install .
308
+ python main.py
309
+ ```
310
+
311
+ Set your provider's API key in `.env` before running - `config.validate()` fails
312
+ fast with a clear error if it's missing.
313
+
314
+ Replace the placeholder agent logic with your actual LLM/framework implementation.
315
+ """
@@ -0,0 +1,370 @@
1
+ FRAMEWORK_FILES = {
2
+ "basic": """[build-system]
3
+ requires = ["setuptools>=68"]
4
+ build-backend = "setuptools.build_meta"
5
+
6
+ [project]
7
+ name = "{{PROJECT_NAME}}"
8
+ version = "0.1.0"
9
+ description = "A multi-agent AI application."
10
+ requires-python = ">=3.9"
11
+ dependencies = []
12
+
13
+ [tool.setuptools]
14
+ package-dir = {"" = "src"}
15
+
16
+ [tool.setuptools.packages.find]
17
+ where = ["src"]
18
+ """,
19
+ "langgraph": """[build-system]
20
+ requires = ["setuptools>=68"]
21
+ build-backend = "setuptools.build_meta"
22
+
23
+ [project]
24
+ name = "{{PROJECT_NAME}}"
25
+ version = "0.1.0"
26
+ description = "A LangGraph multi-agent AI application."
27
+ requires-python = ">=3.9"
28
+ dependencies = [
29
+ "langgraph>=0.2",
30
+ ]
31
+
32
+ [tool.setuptools]
33
+ package-dir = {"" = "src"}
34
+
35
+ [tool.setuptools.packages.find]
36
+ where = ["src"]
37
+ """,
38
+ "crewai": """[build-system]
39
+ requires = ["setuptools>=68"]
40
+ build-backend = "setuptools.build_meta"
41
+
42
+ [project]
43
+ name = "{{PROJECT_NAME}}"
44
+ version = "0.1.0"
45
+ description = "A CrewAI multi-agent AI application."
46
+ requires-python = ">=3.9"
47
+ dependencies = [
48
+ "crewai>=0.80",
49
+ ]
50
+
51
+ [tool.setuptools]
52
+ package-dir = {"" = "src"}
53
+
54
+ [tool.setuptools.packages.find]
55
+ where = ["src"]
56
+ """,
57
+ "autogen": """[build-system]
58
+ requires = ["setuptools>=68"]
59
+ build-backend = "setuptools.build_meta"
60
+
61
+ [project]
62
+ name = "{{PROJECT_NAME}}"
63
+ version = "0.1.0"
64
+ description = "An AutoGen multi-agent AI application."
65
+ requires-python = ">=3.9"
66
+ dependencies = [
67
+ "autogen-agentchat>=0.4",
68
+ ]
69
+
70
+ [tool.setuptools]
71
+ package-dir = {"" = "src"}
72
+
73
+ [tool.setuptools.packages.find]
74
+ where = ["src"]
75
+ """,
76
+ }
77
+
78
+ PROVIDER_ENV_VARS = {
79
+ "openai": "OPENAI_API_KEY",
80
+ "anthropic": "ANTHROPIC_API_KEY",
81
+ "google": "GOOGLE_API_KEY",
82
+ }
83
+
84
+ CONFIG_TEMPLATE = """import os
85
+
86
+ MODEL_NAME = os.getenv("MODEL_NAME", "your-model")
87
+ {{PROVIDER_ENV_VAR}} = os.getenv("{{PROVIDER_ENV_VAR}}")
88
+
89
+
90
+ def validate() -> None:
91
+ \"\"\"Fail fast if the required API key is missing.\"\"\"
92
+ if not {{PROVIDER_ENV_VAR}}:
93
+ raise RuntimeError(
94
+ "Missing required environment variable '{{PROVIDER_ENV_VAR}}' for "
95
+ "provider '{{PROVIDER}}'. Set it in your .env file."
96
+ )
97
+ """
98
+
99
+ AGENT_TEMPLATES = {
100
+ "basic": """class {{AGENT_CLASS}}:
101
+ \"\"\"{{AGENT_NAME}} agent.\"\"\"
102
+
103
+ name = "{{AGENT_NAME}}"
104
+
105
+ def run(self, task: str) -> str:
106
+ # TODO: connect this agent to your LLM/framework.
107
+ return f"[{{AGENT_NAME}}] Completed: {task}"
108
+ """,
109
+ "langgraph": """class {{AGENT_CLASS}}:
110
+ \"\"\"{{AGENT_NAME}} node.\"\"\"
111
+
112
+ name = "{{AGENT_NAME}}"
113
+
114
+ def run(self, task: str) -> str:
115
+ # TODO: connect this node to your LLM/framework.
116
+ return f"[{{AGENT_NAME}}] Completed: {task}"
117
+
118
+ def __call__(self, state: dict) -> dict:
119
+ \"\"\"LangGraph node entry point - transforms and returns the graph state.\"\"\"
120
+ state["result"] = self.run(state["task"])
121
+ state["task"] = state["result"]
122
+ return state
123
+ """,
124
+ "crewai": """from crewai import Agent
125
+
126
+
127
+ class {{AGENT_CLASS}}:
128
+ \"\"\"{{AGENT_NAME}} agent.\"\"\"
129
+
130
+ name = "{{AGENT_NAME}}"
131
+
132
+ def __init__(self) -> None:
133
+ self.agent = Agent(
134
+ role="{{AGENT_NAME}}",
135
+ goal="Complete tasks assigned to the {{AGENT_NAME}} role.",
136
+ backstory="An AI agent specialized in {{AGENT_NAME}} tasks.",
137
+ # TODO: configure an LLM, e.g. llm="gpt-4o-mini".
138
+ allow_delegation=False,
139
+ )
140
+
141
+ def run(self, task: str) -> str:
142
+ # TODO: wrap `task` in a crewai.Task and execute it via a Crew.
143
+ return f"[{{AGENT_NAME}}] Completed: {task}"
144
+ """,
145
+ "autogen": """from autogen_agentchat.agents import AssistantAgent
146
+
147
+
148
+ class {{AGENT_CLASS}}:
149
+ \"\"\"{{AGENT_NAME}} agent.\"\"\"
150
+
151
+ name = "{{AGENT_NAME}}"
152
+
153
+ def build(self, model_client) -> AssistantAgent:
154
+ \"\"\"Build a real AutoGen AssistantAgent once you have a model client.\"\"\"
155
+ return AssistantAgent(
156
+ name="{{AGENT_NAME}}",
157
+ model_client=model_client,
158
+ system_message="You are the {{AGENT_NAME}} agent.",
159
+ )
160
+
161
+ def run(self, task: str) -> str:
162
+ # TODO: connect this agent to your LLM/framework.
163
+ return f"[{{AGENT_NAME}}] Completed: {task}"
164
+ """,
165
+ }
166
+
167
+ ORCHESTRATOR_TEMPLATES = {
168
+ "basic": """from __future__ import annotations
169
+
170
+ import logging
171
+
172
+ logger = logging.getLogger(__name__)
173
+
174
+
175
+ class AgentExecutionError(RuntimeError):
176
+ \"\"\"Raised when an agent fails during orchestration.\"\"\"
177
+
178
+
179
+ class Orchestrator:
180
+ \"\"\"Simple sequential multi-agent orchestrator.\"\"\"
181
+
182
+ def __init__(self, agents):
183
+ self.agents = list(agents)
184
+
185
+ def run(self, task: str) -> str:
186
+ current = task
187
+
188
+ for agent in self.agents:
189
+ logger.info("Running agent: %s", agent.name)
190
+
191
+ try:
192
+ current = agent.run(current)
193
+ except Exception as exc:
194
+ logger.exception("Agent '%s' failed", agent.name)
195
+ raise AgentExecutionError(f"Agent '{agent.name}' failed: {exc}") from exc
196
+
197
+ return current
198
+ """,
199
+ "langgraph": """from __future__ import annotations
200
+
201
+ import logging
202
+
203
+ from langgraph.graph import END, START, StateGraph
204
+
205
+ logger = logging.getLogger(__name__)
206
+
207
+
208
+ class AgentExecutionError(RuntimeError):
209
+ \"\"\"Raised when the LangGraph execution fails.\"\"\"
210
+
211
+
212
+ class Orchestrator:
213
+ \"\"\"LangGraph-based sequential multi-agent orchestrator.\"\"\"
214
+
215
+ def __init__(self, agents):
216
+ self.agents = list(agents)
217
+ self.graph = self._build_graph()
218
+
219
+ def _build_graph(self):
220
+ graph = StateGraph(dict)
221
+
222
+ previous = START
223
+ for index, agent in enumerate(self.agents):
224
+ node_id = f"node_{index}"
225
+ graph.add_node(node_id, agent)
226
+ graph.add_edge(previous, node_id)
227
+ previous = node_id
228
+
229
+ graph.add_edge(previous, END)
230
+
231
+ return graph.compile()
232
+
233
+ def run(self, task: str) -> str:
234
+ logger.info("Invoking LangGraph with %d node(s)", len(self.agents))
235
+
236
+ try:
237
+ final_state = self.graph.invoke({"task": task, "result": ""})
238
+ except Exception as exc:
239
+ logger.exception("LangGraph execution failed")
240
+ raise AgentExecutionError(f"LangGraph execution failed: {exc}") from exc
241
+
242
+ return final_state["result"]
243
+ """,
244
+ "crewai": """from __future__ import annotations
245
+
246
+ import logging
247
+
248
+ from crewai import Crew, Task
249
+
250
+ logger = logging.getLogger(__name__)
251
+
252
+
253
+ class AgentExecutionError(RuntimeError):
254
+ \"\"\"Raised when an agent fails during orchestration.\"\"\"
255
+
256
+
257
+ class Orchestrator:
258
+ \"\"\"CrewAI-based multi-agent orchestrator.\"\"\"
259
+
260
+ def __init__(self, agents):
261
+ self.agents = list(agents)
262
+
263
+ def build_crew(self, task: str) -> Crew:
264
+ \"\"\"Build a real CrewAI Crew for the given task.
265
+
266
+ Once your agents have an LLM configured, use
267
+ `orchestrator.build_crew(task).kickoff()` instead of `run()`.
268
+ \"\"\"
269
+ tasks = [
270
+ Task(
271
+ description=task,
272
+ agent=agent.agent,
273
+ expected_output="A completed response for the assigned step.",
274
+ )
275
+ for agent in self.agents
276
+ ]
277
+
278
+ return Crew(agents=[agent.agent for agent in self.agents], tasks=tasks)
279
+
280
+ def run(self, task: str) -> str:
281
+ current = task
282
+
283
+ for agent in self.agents:
284
+ logger.info("Running agent: %s", agent.name)
285
+
286
+ try:
287
+ current = agent.run(current)
288
+ except Exception as exc:
289
+ logger.exception("Agent '%s' failed", agent.name)
290
+ raise AgentExecutionError(f"Agent '{agent.name}' failed: {exc}") from exc
291
+
292
+ return current
293
+ """,
294
+ "autogen": """from __future__ import annotations
295
+
296
+ import logging
297
+
298
+ from autogen_agentchat.teams import RoundRobinGroupChat
299
+
300
+ logger = logging.getLogger(__name__)
301
+
302
+
303
+ class AgentExecutionError(RuntimeError):
304
+ \"\"\"Raised when an agent fails during orchestration.\"\"\"
305
+
306
+
307
+ class Orchestrator:
308
+ \"\"\"AutoGen-based multi-agent orchestrator.\"\"\"
309
+
310
+ def __init__(self, agents):
311
+ self.agents = list(agents)
312
+
313
+ def build_team(self, model_client) -> RoundRobinGroupChat:
314
+ \"\"\"Build a real AutoGen team once you have a model client.
315
+
316
+ Once your agents have an LLM configured, run the team with
317
+ `await team.run(task=...)` instead of `run()`.
318
+ \"\"\"
319
+ participants = [agent.build(model_client) for agent in self.agents]
320
+
321
+ return RoundRobinGroupChat(participants)
322
+
323
+ def run(self, task: str) -> str:
324
+ current = task
325
+
326
+ for agent in self.agents:
327
+ logger.info("Running agent: %s", agent.name)
328
+
329
+ try:
330
+ current = agent.run(current)
331
+ except Exception as exc:
332
+ logger.exception("Agent '%s' failed", agent.name)
333
+ raise AgentExecutionError(f"Agent '{agent.name}' failed: {exc}") from exc
334
+
335
+ return current
336
+ """,
337
+ }
338
+
339
+ TOOL_FILE = """class WebSearchTool:
340
+ \"\"\"Placeholder for a web-search integration.\"\"\"
341
+
342
+ def search(self, query: str) -> str:
343
+ return f"Search provider placeholder: {query}"
344
+ """
345
+
346
+ TEST_TEMPLATE = """import unittest
347
+
348
+ {{TEST_IMPORTS}}
349
+
350
+
351
+ class AgentTests(unittest.TestCase):
352
+ def test_agents_return_output(self):
353
+ task = "test task"
354
+ {{ASSERTIONS}}
355
+
356
+
357
+ if __name__ == "__main__":
358
+ unittest.main()
359
+ """
360
+
361
+ DOCKER_FILE = """FROM python:3.12-slim
362
+
363
+ WORKDIR /app
364
+
365
+ COPY . .
366
+
367
+ RUN pip install --no-cache-dir .
368
+
369
+ CMD ["python", "-m", "{{PACKAGE_NAME}}"]
370
+ """
@@ -0,0 +1,175 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+
5
+ import questionary
6
+ from questionary import Style
7
+
8
+ NAME_PATTERN = re.compile(r"^[A-Za-z0-9][A-Za-z0-9_-]*$")
9
+ AGENT_PATTERN = re.compile(r"^[A-Za-z][A-Za-z0-9 _-]*$")
10
+
11
+ STYLE = Style(
12
+ [
13
+ ("qmark", "fg:cyan bold"),
14
+ ("question", "bold"),
15
+ ("answer", "fg:cyan"),
16
+ ("pointer", "fg:cyan bold"),
17
+ ("highlighted", "fg:cyan bold"),
18
+ ]
19
+ )
20
+
21
+
22
+ def _valid_project_name(value: str) -> bool:
23
+ return bool(NAME_PATTERN.fullmatch(value.strip()))
24
+
25
+
26
+ def _valid_agent_name(value: str) -> bool:
27
+ return bool(AGENT_PATTERN.fullmatch(value.strip()))
28
+
29
+
30
+ def _ask_agent_names(count: int) -> list[str]:
31
+ names = []
32
+
33
+ for index in range(1, count + 1):
34
+ name = questionary.text(
35
+ f"Agent {index} name:",
36
+ validate=lambda value: (
37
+ True
38
+ if _valid_agent_name(value)
39
+ else "Use letters, numbers, spaces, '-' or '_'."
40
+ ),
41
+ style=STYLE,
42
+ ).ask()
43
+
44
+ if name is None:
45
+ raise KeyboardInterrupt
46
+
47
+ names.append(name.strip())
48
+
49
+ return names
50
+
51
+
52
+ def run_wizard() -> dict:
53
+ print()
54
+ print("🚀 MultiAgent Init")
55
+ print()
56
+
57
+ project_name = questionary.text(
58
+ "Project name:",
59
+ validate=lambda value: (
60
+ True
61
+ if _valid_project_name(value)
62
+ else "Use letters, numbers, '-' or '_' and start with a letter/number."
63
+ ),
64
+ style=STYLE,
65
+ ).ask()
66
+
67
+ if project_name is None:
68
+ raise KeyboardInterrupt
69
+
70
+ framework = questionary.select(
71
+ "Select a framework:",
72
+ choices=[
73
+ questionary.Choice("Basic", value="basic"),
74
+ questionary.Choice("LangGraph", value="langgraph"),
75
+ questionary.Choice("CrewAI", value="crewai"),
76
+ questionary.Choice("AutoGen", value="autogen"),
77
+ ],
78
+ default="basic",
79
+ style=STYLE,
80
+ ).ask()
81
+
82
+ provider = questionary.select(
83
+ "Select model provider:",
84
+ choices=[
85
+ questionary.Choice("OpenAI", value="openai"),
86
+ questionary.Choice("Anthropic", value="anthropic"),
87
+ questionary.Choice("Google", value="google"),
88
+ ],
89
+ default="openai",
90
+ style=STYLE,
91
+ ).ask()
92
+
93
+ agent_count_choice = questionary.select(
94
+ "Number of agents:",
95
+ choices=["3", "2", "4", "5", "Custom"],
96
+ default="3",
97
+ style=STYLE,
98
+ ).ask()
99
+
100
+ if agent_count_choice == "Custom":
101
+ agent_count = questionary.text(
102
+ "How many agents?",
103
+ default="6",
104
+ validate=lambda value: (
105
+ True
106
+ if value.isdigit() and 1 <= int(value) <= 50
107
+ else "Enter a number between 1 and 50."
108
+ ),
109
+ style=STYLE,
110
+ ).ask()
111
+
112
+ if agent_count is None:
113
+ raise KeyboardInterrupt
114
+
115
+ agent_count = int(agent_count)
116
+ else:
117
+ agent_count = int(agent_count_choice)
118
+
119
+ agent_mode = questionary.select(
120
+ "Agent configuration:",
121
+ choices=[
122
+ questionary.Choice("Default agents", value="default"),
123
+ questionary.Choice("Custom agents", value="custom"),
124
+ ],
125
+ default="default",
126
+ style=STYLE,
127
+ ).ask()
128
+
129
+ default_names = [
130
+ "Researcher",
131
+ "Writer",
132
+ "Reviewer",
133
+ "Planner",
134
+ "Executor",
135
+ ]
136
+
137
+ if agent_mode == "default":
138
+ if agent_count <= len(default_names):
139
+ agent_names = default_names[:agent_count]
140
+ else:
141
+ agent_names = default_names[:]
142
+ for index in range(len(default_names) + 1, agent_count + 1):
143
+ agent_names.append(f"Agent {index}")
144
+ else:
145
+ agent_names = _ask_agent_names(agent_count)
146
+
147
+ include_tools = questionary.confirm(
148
+ "Include tools?",
149
+ default=True,
150
+ style=STYLE,
151
+ ).ask()
152
+
153
+ include_tests = questionary.confirm(
154
+ "Include tests?",
155
+ default=True,
156
+ style=STYLE,
157
+ ).ask()
158
+
159
+ include_docker = questionary.confirm(
160
+ "Include Docker?",
161
+ default=False,
162
+ style=STYLE,
163
+ ).ask()
164
+
165
+ return {
166
+ "project_name": project_name.strip(),
167
+ "framework": framework,
168
+ "provider": provider,
169
+ "agent_count": agent_count,
170
+ "agent_mode": agent_mode,
171
+ "agent_names": agent_names,
172
+ "include_tools": include_tools,
173
+ "include_tests": include_tests,
174
+ "include_docker": include_docker,
175
+ }
@@ -0,0 +1,147 @@
1
+ Metadata-Version: 2.4
2
+ Name: multiagent-init
3
+ Version: 0.1.0
4
+ Summary: Interactive CLI for generating multi-agent AI project boilerplates.
5
+ Author-email: Pavan Sekhar Mandavilli <pavanmandavilli2004@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/Pavanmandavilli/multiagent-init
8
+ Project-URL: Repository, https://github.com/Pavanmandavilli/multiagent-init
9
+ Project-URL: Issues, https://github.com/Pavanmandavilli/multiagent-init/issues
10
+ Keywords: multi-agent,agents,ai,scaffolding,cli,generator
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3 :: Only
16
+ Requires-Python: >=3.9
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Requires-Dist: questionary<3.0.0,>=2.0.0
20
+ Dynamic: license-file
21
+
22
+ # 🚀 MultiAgent Init
23
+
24
+ Interactive CLI for generating a clean multi-agent AI project.
25
+
26
+ ## Install
27
+
28
+ ```bash
29
+ pip install multiagent-init
30
+ ```
31
+
32
+ ## Run
33
+
34
+ ```bash
35
+ multiagent-init
36
+ ```
37
+
38
+ ## Wizard
39
+
40
+ The CLI asks for:
41
+
42
+ 1. Project name
43
+ 2. Framework
44
+ 3. Model provider
45
+ 4. Number of agents
46
+ 5. Default or custom agents
47
+ 6. Optional tools
48
+ 7. Optional tests
49
+ 8. Optional Docker
50
+
51
+ ### Example
52
+
53
+ ```text
54
+ 🚀 MultiAgent Init
55
+
56
+ ? Project name: research-team
57
+
58
+ ? Select a framework:
59
+ ❯ Basic
60
+ LangGraph
61
+ CrewAI
62
+ AutoGen
63
+
64
+ ? Select model provider:
65
+ ❯ OpenAI
66
+ Anthropic
67
+ Google
68
+
69
+ ? Number of agents:
70
+ ❯ 3
71
+ 2
72
+ 4
73
+ 5
74
+ Custom
75
+
76
+ ? Agent configuration:
77
+ ❯ Default agents
78
+ Custom agents
79
+
80
+ ? Agent 1 name: Market Researcher
81
+ ? Agent 2 name: Data Analyst
82
+ ? Agent 3 name: Report Writer
83
+
84
+ ? Include tools? Yes
85
+ ? Include tests? Yes
86
+ ? Include Docker? No
87
+
88
+ Creating project...
89
+
90
+ ✓ Agents
91
+ ✓ Workflow
92
+ ✓ Tools
93
+ ✓ Configuration
94
+ ✓ Tests
95
+ ✓ README
96
+
97
+ 🎉 Done!
98
+ ```
99
+
100
+ ## Generated project
101
+
102
+ Custom agent names are converted into safe Python filenames and class names.
103
+
104
+ Example:
105
+
106
+ ```text
107
+ Market Researcher → market_researcher.py → MarketResearcherAgent
108
+ Data Analyst → data_analyst.py → DataAnalystAgent
109
+ Report Writer → report_writer.py → ReportWriterAgent
110
+ ```
111
+
112
+ ## Development
113
+
114
+ ```bash
115
+ python3 -m venv .venv
116
+ source .venv/bin/activate
117
+ python -m pip install -e .
118
+ python -m unittest discover -s tests -v
119
+ ```
120
+
121
+ Build:
122
+
123
+ ```bash
124
+ python -m pip install --upgrade build
125
+ python -m build
126
+ ```
127
+
128
+ ## Publish
129
+
130
+ TestPyPI:
131
+
132
+ ```bash
133
+ python -m pip install --upgrade twine
134
+ python -m twine upload --repository testpypi dist/*
135
+ ```
136
+
137
+ PyPI:
138
+
139
+ ```bash
140
+ python -m twine upload dist/*
141
+ ```
142
+
143
+ For CI/CD, configure PyPI Trusted Publishing with GitHub Actions.
144
+
145
+ ## License
146
+
147
+ MIT
@@ -0,0 +1,11 @@
1
+ multiagent_init/__init__.py,sha256=kUR5RAFc7HCeiqdlX36dZOHkUI5wI6V_43RpEcD8b-0,22
2
+ multiagent_init/cli.py,sha256=KSCkKerzzimHHFErQLfy2bFYv9x5VDDyyO6ki0vT9Fg,842
3
+ multiagent_init/generator.py,sha256=1AONIxfB9dYToaEcJqJKdpQoFetQGsmF_RnfQTsOSJs,7726
4
+ multiagent_init/templates_data.py,sha256=qRWw9vVceVjkt9QVcihFA4nvJ1d4KYDEUkD2myhwBFk,9468
5
+ multiagent_init/wizard.py,sha256=3d3YA9e1MFIf00KdbOlA9w-kYvBEYPsipZUDzAhClRc,4527
6
+ multiagent_init-0.1.0.dist-info/licenses/LICENSE,sha256=qAWvkf1ylseZfzIyN09FEw7XxS6MDeJ7ZiL1qH5fVqA,1081
7
+ multiagent_init-0.1.0.dist-info/METADATA,sha256=6dHalPlVeoBdroiWLRgzm6zeROkd5o2h4MeAEi6wBzc,2628
8
+ multiagent_init-0.1.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
9
+ multiagent_init-0.1.0.dist-info/entry_points.txt,sha256=-Yzt8FY_8k0fuLS7zS3oqEvmLX-qqVQYXUKL42mJjsY,61
10
+ multiagent_init-0.1.0.dist-info/top_level.txt,sha256=rLfcUGuLZ-VSZxeFphJKtzsXupC2ZObn5b0TEJrAwJI,16
11
+ multiagent_init-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ multiagent-init = multiagent_init.cli:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mandavilli. Pavan sekhar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ multiagent_init