Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
75f404f
Feat: Chatbot template
Abhiix0 Jun 27, 2026
ad9d647
update: registry and tests
Abhiix0 Jun 27, 2026
eead5f3
update: test_prompts
Abhiix0 Jun 27, 2026
2964b8c
Docs: update the docs
Abhiix0 Jun 27, 2026
86e41ea
chatbot v1
Abhiix0 Jun 27, 2026
3a1b4f5
Framework-Specific .env.example + Content Fix
Abhiix0 Jun 27, 2026
14eae01
fix: llm content
Abhiix0 Jun 27, 2026
7b716bc
fix: chatbot v2
Abhiix0 Jun 28, 2026
1e4abee
Add provider field to ProjectConfig and TemplateMetadata
Abhiix0 Jun 28, 2026
0de241a
Add provider prompt to prompts.py
Abhiix0 Jun 28, 2026
dbddb7d
Rebuild chatbot/content.py with all framework×provider combinations
Abhiix0 Jun 28, 2026
8dedd7d
Rebuild chatbot/__init__.py
Abhiix0 Jun 28, 2026
25349bf
Rewrite test_chatbot_template.py
Abhiix0 Jun 28, 2026
217d74d
Update test_prompts.py for provider prompt
Abhiix0 Jun 28, 2026
510482d
Update changelog
Abhiix0 Jun 28, 2026
f6b9055
Tests: updates the test
Abhiix0 Jun 28, 2026
c2ef9a2
Fix pydantic-ai LLM Content Strings
Abhiix0 Jun 28, 2026
e8c561c
Content String Tests
Abhiix0 Jun 28, 2026
85bb295
Content String Tests 1
Abhiix0 Jun 28, 2026
5d6e994
Chatbot v3
Abhiix0 Jul 4, 2026
7a959b6
PydanticAI Multi-Turn Memory
Abhiix0 Jul 4, 2026
5af8e71
Dependencies + Remove Utils
Abhiix0 Jul 4, 2026
1c216e6
meta.json Provider + next_steps Fix
Abhiix0 Jul 4, 2026
b18c27b
conftest.py + TEST_CONTENT Update
Abhiix0 Jul 4, 2026
1f05092
Full test suite
Abhiix0 Jul 4, 2026
5c37c42
Add Groq
Abhiix0 Jul 5, 2026
a64e728
Wire Groq into __init__.py
Abhiix0 Jul 5, 2026
ee92d04
Registry + prompts.py PROVIDER_MAP
Abhiix0 Jul 5, 2026
26925a7
tests for groq
Abhiix0 Jul 5, 2026
db17fbe
GROQ dep issues
Abhiix0 Jul 6, 2026
630bd75
Fix README
Abhiix0 Jul 6, 2026
bf4f1be
Make memory/history.py framework-conditional
Abhiix0 Jul 6, 2026
a7f6ec3
Remove dead get_model()
Abhiix0 Jul 6, 2026
de64d17
Single source of truth for framework/provider validity
Abhiix0 Jul 6, 2026
e2cfcd7
Changelog update
Abhiix0 Jul 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 30 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ It's repetitive. It's inconsistent. And you haven't written a single line of *re

| Feature | What it does |
|---|---|
| **Intent-based templates** | Backend API (FastAPI / Flask / Django), CLI Application (Typer / Click / Argparse), Automation Tool |
| **Intent-based templates** | Backend API (FastAPI / Flask / Django), CLI Application (Typer / Click / Argparse), Automation Tool, AI Chatbot |
| **Extras system** | Opt-in ruff, pytest, Docker, GitHub Actions — installed and wired automatically |
| **Dependency installation** | `uv add` runs automatically with the right packages for your choices |
| **Git + uv** | Optionally runs `git init`, `uv init`, and `uv venv` |
Expand Down Expand Up @@ -91,8 +91,9 @@ Spawn rejects names with spaces or special characters, and tells you immediately
1 Backend API
2 CLI Application
3 Automation Tool
4 AI Chatbot

Choose Template [1-3]: 1
Choose Template [1-4]: 1
```

**Step 3 — Additional prompts** *(template-dependent)*
Expand Down Expand Up @@ -273,6 +274,31 @@ uv run python -m src.main

---

### `[4]` AI Chatbot

Best for: customer support bots, study assistants, conversational AI tools.

```
my-chatbot/
├── src/
│ ├── chatbot/
│ ├── providers/
│ ├── prompts/
│ ├── utils/
│ └── main.py
├── tests/
├── .env.example
└── README.md
```

```bash
cd my-chatbot
# Add API_KEY to .env
uv run python -m src.main
```

---

## Other Commands

### `spawn doctor`
Expand Down Expand Up @@ -315,7 +341,7 @@ spawn doctor ./path/to/project

```bash
spawn version
# → Spawn v0.5.0
# → Spawn v0.6.0
```

### Publish to GitHub
Expand Down Expand Up @@ -351,7 +377,7 @@ All tests should pass. If they don't, please [open an issue](https://github.com/
- [x] **Dependency installation** — `uv add` runs automatically after generation (v0.3.0)
- [x] **CLI Application intent** — Typer, Click, Argparse with Utility/Interactive sub-types (v0.4.0)
- [x] **Automation Tool intent** — workflow-based automation with logging, tasks, and integrations (v0.5.0)
- [ ] **AI Chatbot intent** — LLM-integrated chat app scaffold (v0.6.0)
- [x] **AI Chatbot intent** — PydanticAI and OpenAI SDK with provider abstraction (v0.6.0)
- [ ] **AI Agent intent** — tool-calling agent scaffold (v0.7.0)
- [ ] **RAG System intent** — retrieval-augmented generation scaffold (v0.8.0)
- [ ] **Data Project intent** — analysis, dashboard, ETL, ML sub-options (v0.9.0)
Expand Down
6 changes: 5 additions & 1 deletion docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Entry point: `spawn.cli.app:main` (defined in `pyproject.toml` as `[project.scri

```
src/spawn/
├── __init__.py # __version__ via importlib.metadata, fallback "0.5.0"
├── __init__.py # __version__ via importlib.metadata, fallback "0.6.0"
├── cli/
│ ├── app.py # Typer app: create, version, doctor commands
│ └── prompts.py # Interactive prompts; derives menu from registry
Expand All @@ -50,6 +50,9 @@ src/spawn/
│ ├── automation/
│ │ ├── __init__.py # AutomationTemplate
│ │ └── content.py # Workflow, task, logger, test, README content strings
│ ├── chatbot/
│ │ ├── __init__.py # ChatbotTemplate (branches on framework)
│ │ └── content.py # LLM provider, chat, prompt, test, README strings
│ ├── python_script/ # Reserved — not in active menu
│ ├── data_science/ # Reserved — not in active menu
│ └── ml_project/ # Reserved — not in active menu
Expand Down Expand Up @@ -137,6 +140,7 @@ Each template lives in its own subdirectory with an `__init__.py` (class) and `c
| `backend-api` | `BackendAPITemplate` | fastapi, flask, django | ruff, pytest, docker, github-actions |
| `cli` | `CLITemplate` | typer, click, argparse | ruff, pytest, github-actions |
| `automation` | `AutomationTemplate` | none | ruff, pytest, github-actions |
| `chatbot` | `ChatbotTemplate` | pydantic-ai, openai-sdk | ruff, pytest, github-actions |

`get_template(slug)` returns a default-constructed instance. `instantiate_template(config)` forwards `framework`, `extras`, and `cli_type` from `ProjectConfig` to templates whose constructors accept them, using signature introspection.

Expand Down
35 changes: 35 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,41 @@
All notable changes to Spawn are documented here.
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## v0.6.0 — 2026

### New Features

- **AI Chatbot intent** — generates a fully runnable conversational AI project
with runtime memory, centralized prompt management, and provider abstraction
- **3 frameworks**: PydanticAI, OpenAI SDK, LiteLLM
- **6 providers**: OpenAI, Anthropic, Gemini, OpenRouter, Ollama, Groq
- **16 supported combinations** — each generates correct dependencies,
provider-specific env vars, and working llm.py out of the box
- **Runtime memory** — `src/memory/history.py` maintains conversation
context across turns within a session; no database required
- **Plain-text prompt system** — `src/prompts/system.txt` is editable
without touching Python code; loaded dynamically at runtime
- **Rich extra** — opt-in `rich` terminal UI with colored panels and
styled input/output
- **Provider-specific env vars** — generated `.env.example` uses the
correct key name for each provider (OPENAI_API_KEY, ANTHROPIC_API_KEY,
GOOGLE_API_KEY, OPENROUTER_API_KEY, OLLAMA_BASE_URL)

### Bug Fixes

- Fixed `result.data` → `result.output` in PydanticAI provider
(AgentRunResult attribute name in current pydantic-ai)
- Fixed `setdefault("OPENAI_API_KEY")` pattern that silently broke
non-OpenAI providers; api_key is now passed directly to run_sync()

### Internal

- `ProjectConfig` gains `provider: str | None = None` field
- `TemplateMetadata` gains `available_providers: list[str]` field
- `instantiate_template()` forwards `provider` to template constructors
- Registry: chatbot updated to 3 frameworks, 5 providers, 4 extras
- Version bumped to 0.6.0

## v0.5.0 — 2026

### New Features
Expand Down
31 changes: 28 additions & 3 deletions docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Creates a new project directory from a template, writes starter files, installs
| Step | Prompt | When shown |
|---|---|---|
| 1 | `Project Name` | Always |
| 2 | Template list → `Choose Template [1-3]` | Always |
| 2 | Template list → `Choose Template [1-4]` | Always |
| 3 | CLI Type list → `Choose CLI Type [1-2]` | Only for CLI Application |
| 4 | Framework list → `Choose Framework [1-N]` | Only for templates with frameworks |
| 5 | Extras list → `Extras` | Only for templates with extras |
Expand Down Expand Up @@ -49,6 +49,7 @@ Templates are displayed as a numbered list. The current registry order:
| `1` | `backend-api` | Backend API — production-ready FastAPI, Flask, or Django |
| `2` | `cli` | CLI Application — Typer, Click, or Argparse with Utility or Interactive type |
| `3` | `automation` | Automation Tool — workflow-based automation with logging and tasks |
| `4` | `chatbot` | AI Chatbot — PydanticAI or OpenAI SDK with provider abstraction |

**Invalid input error (exact):**
```
Expand Down Expand Up @@ -361,6 +362,30 @@ uv run python -m src.main

---

### AI Chatbot intent

Selecting AI Chatbot triggers a framework prompt then extras.

#### Framework selection

```
1 pydantic-ai
2 openai-sdk

Choose Framework [1-2]:
```

| Input | Framework | Key dependency |
|---|---|---|
| `1` (default) | PydanticAI | `pydantic-ai` |
| `2` | OpenAI SDK | `openai` |

Both variants install `python-dotenv` and use the same project structure.

Provider switching requires changes only in `src/providers/llm.py` and `.env`.

---

### `.spawn/meta.json`

Every generated project receives a `.spawn/meta.json` file:
Expand All @@ -369,7 +394,7 @@ Every generated project receives a `.spawn/meta.json` file:
{
"intent": "backend-api",
"framework": "fastapi",
"spawn_version": "0.5.0"
"spawn_version": "0.6.0"
}
```

Expand Down Expand Up @@ -443,7 +468,7 @@ Prints the installed package version.
**Output (exact):**

```
Spawn v0.5.0
Spawn v0.6.0
```

---
Expand Down
8 changes: 5 additions & 3 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ $ spawn create
1 Backend API
2 CLI Application
3 Automation Tool
4 AI Chatbot

Choose Template [1-3]: 2
Choose Template [1-4]: 2

1 utility
2 interactive
Expand Down Expand Up @@ -81,8 +82,9 @@ $ spawn create
1 Backend API
2 CLI Application
3 Automation Tool
4 AI Chatbot

Choose Template [1-3]: 1
Choose Template [1-4]: 1

1 fastapi
2 flask
Expand Down Expand Up @@ -165,7 +167,7 @@ spawn version
```

```
Spawn v0.5.0
Spawn v0.6.0
```

```bash
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "spawn"
version = "0.5.0"
version = "0.6.0"
description = "Spawn a ready-to-use Python project in seconds"
readme = "README.md"
requires-python = ">=3.12"
Expand Down
2 changes: 1 addition & 1 deletion src/spawn/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
try:
__version__ = version("spawn")
except PackageNotFoundError:
__version__ = "0.5.0"
__version__ = "0.6.0"
40 changes: 40 additions & 0 deletions src/spawn/cli/prompts.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from spawn.utils.console import console
from spawn.core.models import ProjectConfig
from spawn.core.registry import list_templates, get_metadata
from spawn.templates.chatbot import get_supported_providers
from spawn.utils.validators import validate_project_name
from spawn.core.exceptions import SpawnError

Expand Down Expand Up @@ -69,6 +70,7 @@ def get_project_config() -> ProjectConfig:
# --- Framework selection ---
selected_framework: str | None = None
selected_cli_type: str | None = None
selected_provider: str | None = None
meta = get_metadata(template)

# --- CLI type selection ---
Expand Down Expand Up @@ -130,6 +132,43 @@ def get_project_config() -> ProjectConfig:

selected_framework = framework_map[fw_choice]

# --- Provider selection ---
if meta and meta.available_providers and selected_framework:
provider_options = (
get_supported_providers(selected_framework)
if selected_framework
else meta.available_providers
)
provider_choice_map = {
str(i): p for i, p in enumerate(provider_options, start=1)
}

_print_list(provider_options)

valid_prov_range = len(provider_options)
prov_choice = typer.prompt(
typer.style(
f"Choose Provider [1-{valid_prov_range}]",
fg=typer.colors.CYAN,
),
default="1",
)

while prov_choice not in provider_choice_map:
typer.secho(
"Invalid choice. Please select a valid number.",
fg=typer.colors.RED,
)
prov_choice = typer.prompt(
typer.style(
f"Choose Provider [1-{valid_prov_range}]",
fg=typer.colors.CYAN,
),
default="1",
)

selected_provider = provider_choice_map[prov_choice]

# --- Extras selection ---
selected_extras: list[str] = []

Expand Down Expand Up @@ -175,4 +214,5 @@ def get_project_config() -> ProjectConfig:
framework=selected_framework,
extras=selected_extras,
cli_type=selected_cli_type,
provider=selected_provider,
)
1 change: 1 addition & 0 deletions src/spawn/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ class ProjectConfig:
framework: str | None = None
extras: list[str] = field(default_factory=list)
cli_type: str | None = None
provider: str | None = None
13 changes: 13 additions & 0 deletions src/spawn/core/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from spawn.templates.backend_api import BackendAPITemplate
from spawn.templates.cli_application import CLITemplate
from spawn.templates.automation import AutomationTemplate
from spawn.templates.chatbot import ChatbotTemplate
from spawn.templates.base import BaseTemplate


Expand All @@ -21,6 +22,7 @@ class TemplateMetadata:
available_frameworks: list[str] = field(default_factory=list)
available_extras: list[str] = field(default_factory=list)
available_cli_types: list[str] = field(default_factory=list)
available_providers: list[str] = field(default_factory=list)


# Slugs that existed in previous versions but have been superseded.
Expand Down Expand Up @@ -53,6 +55,15 @@ class TemplateMetadata:
template_class=AutomationTemplate,
available_extras=["ruff", "pytest", "github-actions"],
),
"chatbot": TemplateMetadata(
slug="chatbot",
display_name="AI Chatbot",
description="Conversational AI with PydanticAI, OpenAI SDK, or LiteLLM",
template_class=ChatbotTemplate,
available_frameworks=["pydantic-ai", "openai-sdk", "litellm"],
available_providers=["openai", "anthropic", "gemini", "openrouter", "ollama", "groq"],
available_extras=["ruff", "pytest", "rich", "github-actions"],
),
}


Expand Down Expand Up @@ -93,6 +104,8 @@ def instantiate_template(config: ProjectConfig) -> BaseTemplate | None:
kwargs["extras"] = config.extras
if "cli_type" in params:
kwargs["cli_type"] = config.cli_type
if "provider" in params:
kwargs["provider"] = config.provider

return cls(**kwargs)

Expand Down
1 change: 1 addition & 0 deletions src/spawn/generators/project_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ def generate(self, config: ProjectConfig) -> Path:
{
"intent": config.template,
"framework": config.framework,
"provider": config.provider,
"spawn_version": __version__,
},
indent=2,
Expand Down
Loading
Loading