spawnio 1.0.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 (56) hide show
  1. spawn/__init__.py +6 -0
  2. spawn/cli/__init__.py +0 -0
  3. spawn/cli/app.py +218 -0
  4. spawn/cli/noninteractive.py +204 -0
  5. spawn/cli/prompts.py +267 -0
  6. spawn/core/__init__.py +0 -0
  7. spawn/core/exceptions.py +7 -0
  8. spawn/core/models.py +21 -0
  9. spawn/core/registry.py +152 -0
  10. spawn/generators/__init__.py +0 -0
  11. spawn/generators/custom_structure.py +508 -0
  12. spawn/generators/project_generator.py +115 -0
  13. spawn/github/__init__.py +0 -0
  14. spawn/github/exceptions.py +5 -0
  15. spawn/github/publisher.py +69 -0
  16. spawn/github/validators.py +23 -0
  17. spawn/templates/__init__.py +0 -0
  18. spawn/templates/agent/__init__.py +182 -0
  19. spawn/templates/agent/content.py +374 -0
  20. spawn/templates/automation/__init__.py +100 -0
  21. spawn/templates/automation/content.py +181 -0
  22. spawn/templates/backend_api/__init__.py +243 -0
  23. spawn/templates/backend_api/content.py +509 -0
  24. spawn/templates/base.py +58 -0
  25. spawn/templates/chatbot/__init__.py +243 -0
  26. spawn/templates/chatbot/content.py +668 -0
  27. spawn/templates/cli_application/__init__.py +211 -0
  28. spawn/templates/cli_application/content.py +512 -0
  29. spawn/templates/data_project/__init__.py +125 -0
  30. spawn/templates/data_project/analysis/__init__.py +74 -0
  31. spawn/templates/data_project/analysis/content.py +190 -0
  32. spawn/templates/data_project/dashboard/__init__.py +61 -0
  33. spawn/templates/data_project/dashboard/content.py +135 -0
  34. spawn/templates/data_project/etl/__init__.py +66 -0
  35. spawn/templates/data_project/etl/content.py +239 -0
  36. spawn/templates/data_project/ml/__init__.py +62 -0
  37. spawn/templates/data_project/ml/content.py +239 -0
  38. spawn/templates/mcp_server/__init__.py +83 -0
  39. spawn/templates/mcp_server/content.py +161 -0
  40. spawn/templates/rag/__init__.py +124 -0
  41. spawn/templates/rag/content.py +463 -0
  42. spawn/templates/shared_content.py +89 -0
  43. spawn/utils/__init__.py +0 -0
  44. spawn/utils/banner.py +41 -0
  45. spawn/utils/console.py +3 -0
  46. spawn/utils/doctor.py +594 -0
  47. spawn/utils/git.py +132 -0
  48. spawn/utils/success.py +47 -0
  49. spawn/utils/theme.py +25 -0
  50. spawn/utils/uv.py +61 -0
  51. spawn/utils/validators.py +15 -0
  52. spawnio-1.0.5.dist-info/METADATA +798 -0
  53. spawnio-1.0.5.dist-info/RECORD +56 -0
  54. spawnio-1.0.5.dist-info/WHEEL +4 -0
  55. spawnio-1.0.5.dist-info/entry_points.txt +2 -0
  56. spawnio-1.0.5.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,161 @@
1
+ INIT_CONTENT = ""
2
+
3
+ SERVER_CONTENT = """\
4
+ \"\"\"MCP server entrypoint for {project_name}.\"\"\"
5
+
6
+ from mcp.server import MCPServer
7
+
8
+ mcp = MCPServer("{project_name}")
9
+
10
+
11
+ def _add(a: int, b: int) -> int:
12
+ return a + b
13
+
14
+
15
+ @mcp.tool()
16
+ def add(a: int, b: int) -> int:
17
+ \"\"\"Add two numbers together.\"\"\"
18
+ return _add(a, b)
19
+
20
+
21
+ def _get_server_info() -> str:
22
+ return "{project_name} MCP server, built with Spawn."
23
+
24
+
25
+ @mcp.resource("info://server")
26
+ def server_info() -> str:
27
+ \"\"\"Return basic information about this MCP server.\"\"\"
28
+ return _get_server_info()
29
+
30
+
31
+ def main() -> None:
32
+ mcp.run()
33
+
34
+
35
+ if __name__ == "__main__":
36
+ main()
37
+ """
38
+
39
+ TEST_CONTENT = """\
40
+ from src.server import _add, _get_server_info
41
+
42
+
43
+ def test_add():
44
+ assert _add(2, 3) == 5
45
+
46
+
47
+ def test_add_negative_numbers():
48
+ assert _add(-1, -1) == -2
49
+
50
+
51
+ def test_server_info_returns_nonempty_string():
52
+ info = _get_server_info()
53
+ assert isinstance(info, str)
54
+ assert len(info) > 0
55
+ """
56
+
57
+ ENV_CONTENT = """\
58
+ # Environment variables for {project_name}
59
+ # This MCP server has no required environment variables by default.
60
+ # Add any secrets or configuration your tools/resources need here.
61
+ """
62
+
63
+ GITHUB_ACTIONS_CI_BASE = """\
64
+ name: CI
65
+
66
+ on:
67
+ push:
68
+ branches: [main]
69
+ pull_request:
70
+ branches: [main]
71
+
72
+ jobs:
73
+ ci:
74
+ runs-on: ubuntu-latest
75
+
76
+ steps:
77
+ - uses: actions/checkout@v4
78
+
79
+ - uses: astral-sh/setup-uv@v5
80
+
81
+ - name: Install dependencies
82
+ run: uv sync
83
+ """
84
+
85
+ GITHUB_ACTIONS_CI_RUFF_STEP = """\
86
+ - name: Lint
87
+ run: uv run ruff check .
88
+ """
89
+
90
+ GITHUB_ACTIONS_CI_PYTEST_STEP = """\
91
+ - name: Test
92
+ run: uv run pytest
93
+ """
94
+
95
+
96
+ def make_readme() -> str:
97
+ return """\
98
+ # {project_name}
99
+
100
+ An MCP (Model Context Protocol) server, generated with Spawn.
101
+
102
+ This server exposes one example tool (`add`) and one example resource
103
+ (`info://server`) over the `stdio` transport, ready to connect to any
104
+ MCP client — Claude Desktop, Claude Code, or your own client built on
105
+ the MCP SDK.
106
+
107
+ ## Run it directly
108
+
109
+ ```bash
110
+ uv run python -m src.server
111
+ ```
112
+
113
+ It will wait on stdio for an MCP client to connect — this is normal,
114
+ it's not meant to be run standalone in a terminal for long.
115
+
116
+ ## Connect it to Claude Desktop
117
+
118
+ Add this to your `claude_desktop_config.json`:
119
+
120
+ ```json
121
+ {{
122
+ "mcpServers": {{
123
+ "{project_name}": {{
124
+ "command": "uv",
125
+ "args": ["--directory", "/absolute/path/to/{project_name}", "run", "python", "-m", "src.server"]
126
+ }}
127
+ }}
128
+ }}
129
+ ```
130
+
131
+ Replace `/absolute/path/to/{project_name}` with this project's actual
132
+ path, then restart Claude Desktop.
133
+
134
+ ## Add your own tools and resources
135
+
136
+ Open `src/server.py`. Add a new function decorated with `@mcp.tool()`
137
+ for anything you want an MCP client to be able to call, or
138
+ `@mcp.resource("your://uri")` for anything you want it to be able to
139
+ read.
140
+
141
+ ## Tests
142
+
143
+ ```bash
144
+ uv run pytest
145
+ ```
146
+ """
147
+
148
+
149
+ def make_agents_md() -> str:
150
+ return """\
151
+ # {project_name}
152
+
153
+ An MCP (Model Context Protocol) server built using the `mcp` SDK's `MCPServer` class.
154
+
155
+ ## Adding Tools and Resources
156
+
157
+ Open `src/server.py`.
158
+
159
+ - Add a new tool: Decorate a function with `@mcp.tool()` to expose callable tools to MCP clients.
160
+ - Add a new resource: Decorate a function with `@mcp.resource("uri")` to expose readable resources to MCP clients.
161
+ """
@@ -0,0 +1,124 @@
1
+ from pathlib import Path
2
+
3
+ from spawn.templates.base import BaseTemplate
4
+ from spawn.templates.rag.content import (
5
+ INIT_CONTENT,
6
+ SAMPLE_KNOWLEDGE_CONTENT,
7
+ SETTINGS_CONTENT,
8
+ KNOWLEDGE_INDEX_CONTENT,
9
+ INGESTION_CONTENT,
10
+ RETRIEVAL_CONTENT,
11
+ MAIN_CONTENT,
12
+ TEST_CONTENT,
13
+ CONFTEST_CONTENT,
14
+ ENV_CONTENT,
15
+ GITHUB_ACTIONS_CI_BASE,
16
+ GITHUB_ACTIONS_CI_RUFF_STEP,
17
+ GITHUB_ACTIONS_CI_PYTEST_STEP,
18
+ make_readme,
19
+ make_agents_md,
20
+ )
21
+
22
+ RAG_FOLDERS = [
23
+ "data",
24
+ "chroma_db",
25
+ "src/ingestion",
26
+ "src/retrieval",
27
+ "src/knowledge",
28
+ "src/config",
29
+ "tests",
30
+ ]
31
+
32
+
33
+ def _build_files() -> list:
34
+ return [
35
+ ("data/sample_knowledge.md", SAMPLE_KNOWLEDGE_CONTENT),
36
+ ("src/__init__.py", INIT_CONTENT),
37
+ ("src/config/__init__.py", INIT_CONTENT),
38
+ ("src/config/settings.py", SETTINGS_CONTENT),
39
+ ("src/knowledge/__init__.py", INIT_CONTENT),
40
+ ("src/knowledge/index.py", KNOWLEDGE_INDEX_CONTENT),
41
+ ("src/ingestion/__init__.py", INIT_CONTENT),
42
+ ("src/ingestion/ingest.py", INGESTION_CONTENT),
43
+ ("src/retrieval/__init__.py", INIT_CONTENT),
44
+ ("src/retrieval/retrieve.py", RETRIEVAL_CONTENT),
45
+ ("src/main.py", MAIN_CONTENT),
46
+ ("tests/__init__.py", INIT_CONTENT),
47
+ ("tests/conftest.py", CONFTEST_CONTENT),
48
+ ("tests/test_rag.py", TEST_CONTENT),
49
+ (".env.example", ENV_CONTENT),
50
+ ]
51
+
52
+
53
+ class RAGTemplate(BaseTemplate):
54
+ def __init__(self, extras: list[str] | None = None) -> None:
55
+ self.extras = extras or []
56
+
57
+ super().__init__(
58
+ name="RAG System",
59
+ folders=list(RAG_FOLDERS),
60
+ starter_files=_build_files(),
61
+ next_steps=[
62
+ "cd {project_name}",
63
+ "Rename .env.example to .env and add your OPENAI_API_KEY",
64
+ "uv run python -m src.main",
65
+ ],
66
+ )
67
+
68
+ def get_readme_content(self, context: dict) -> str | None:
69
+ return make_readme().format_map(context)
70
+
71
+ def get_agents_md_content(self, context: dict) -> str | None:
72
+ return make_agents_md().format_map(context)
73
+
74
+ def get_dependencies(self) -> list[str]:
75
+ base = [
76
+ "llama-index-core",
77
+ "llama-index-vector-stores-chroma",
78
+ "llama-index-embeddings-openai",
79
+ "llama-index-llms-openai",
80
+ "chromadb",
81
+ "python-dotenv",
82
+ ]
83
+ if "pytest" in self.extras:
84
+ base.append("pytest")
85
+ if "ruff" in self.extras:
86
+ base.append("ruff")
87
+ return base
88
+
89
+ def post_install(self, project_path: Path) -> None:
90
+ pyproject = project_path / "pyproject.toml"
91
+ current = pyproject.read_text(encoding="utf-8")
92
+ additions = ""
93
+
94
+ if "pytest" in self.extras:
95
+ additions += "\n[tool.pytest.ini_options]\ntestpaths = [\"tests\"]\n"
96
+
97
+ if "ruff" in self.extras:
98
+ additions += "\n[tool.ruff]\nline-length = 88\n"
99
+
100
+ if additions:
101
+ pyproject.write_text(current + additions, encoding="utf-8")
102
+
103
+ if "github-actions" in self.extras:
104
+ workflows_path = project_path / ".github" / "workflows"
105
+ workflows_path.mkdir(parents=True, exist_ok=True)
106
+ ci = GITHUB_ACTIONS_CI_BASE
107
+ if "ruff" in self.extras:
108
+ ci += GITHUB_ACTIONS_CI_RUFF_STEP
109
+ if "pytest" in self.extras:
110
+ ci += GITHUB_ACTIONS_CI_PYTEST_STEP
111
+ (workflows_path / "ci.yml").write_text(ci, encoding="utf-8")
112
+
113
+ # Write .gitkeep into chroma_db/ so git tracks the directory
114
+ # but the actual DB files are gitignored
115
+ gitkeep = project_path / "chroma_db" / ".gitkeep"
116
+ gitkeep.write_text("", encoding="utf-8")
117
+
118
+ gitignore_path = project_path / ".gitignore"
119
+ current_gitignore = gitignore_path.read_text(encoding="utf-8")
120
+ rag_ignore_rules = "\n# RAG vector database\nchroma_db/*\n!chroma_db/.gitkeep\n"
121
+ if "chroma_db/*" not in current_gitignore:
122
+ gitignore_path.write_text(
123
+ current_gitignore + rag_ignore_rules, encoding="utf-8"
124
+ )