Skip to content

Commit 6650173

Browse files
authored
Add base template override support for remote templates (#453)
* feat: add base template override support for remote templates Enable users to override the base template specified in remote templates using the --base-template flag. This allows using different foundational agents while preserving remote template logic and customizations. Key changes: - Add --base-template CLI flag to create command - Implement interactive dependency management via uv add - Validate base template exists before processing - Auto-prompt for required dependencies with skip option - Support --auto-approve for non-interactive workflows - Add comprehensive test coverage - Update documentation with usage examples When overriding a base template, users are prompted to install any additional dependencies required by the new base. Dependencies are added using uv add with proper version resolution. * feat: add --base-template flag with interactive dependency management Allows overriding base template for remote templates. CLI prompts users to install required dependencies via uv add, with support for auto-approve mode. Includes validation, error handling, tests, and documentation.
1 parent 9894b41 commit 6650173

File tree

6 files changed

+384
-2
lines changed

6 files changed

+384
-2
lines changed

agent_starter_pack/cli/commands/create.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
parse_agent_spec,
3838
)
3939
from ..utils.template import (
40+
add_base_template_dependencies_interactively,
4041
get_available_agents,
4142
get_deployment_targets,
4243
get_template_path,
@@ -112,6 +113,11 @@ def shared_template_options(f: Callable) -> Callable:
112113
"-dir",
113114
help="Name of the agent directory (overrides template default)",
114115
)(f)
116+
f = click.option(
117+
"--base-template",
118+
"-bt",
119+
help="Base template to use (overrides template default, only for remote templates)",
120+
)(f)
115121
return f
116122

117123

@@ -474,6 +480,18 @@ def create(
474480
if cli_overrides is None:
475481
cli_overrides = {}
476482
if base_template:
483+
# Validate that the base template exists
484+
if not validate_base_template(base_template):
485+
available_templates = get_available_base_templates()
486+
console.print(
487+
f"Error: Base template '{base_template}' not found.",
488+
style="bold red",
489+
)
490+
console.print(
491+
f"Available base templates: {', '.join(available_templates)}",
492+
style="yellow",
493+
)
494+
raise click.Abort()
477495
cli_overrides["base_template"] = base_template
478496

479497
# Load remote template config
@@ -727,6 +745,24 @@ def create(
727745
# Replace region in all files if a different region was specified
728746
if region != "us-central1":
729747
replace_region_in_files(project_path, region, debug=debug)
748+
749+
# Handle base template dependencies if override was used
750+
if base_template and template_source_path and remote_config:
751+
# Load base template config to get extra_dependencies
752+
base_template_path = get_template_path(base_template, debug=debug)
753+
base_config = load_template_config(base_template_path)
754+
base_deps = base_config.get("settings", {}).get(
755+
"extra_dependencies", []
756+
)
757+
758+
if base_deps:
759+
# Call interactive dependency addition
760+
add_base_template_dependencies_interactively(
761+
project_path,
762+
base_deps,
763+
base_template,
764+
auto_approve=auto_approve,
765+
)
730766
finally:
731767
# Clean up the temporary directory if one was created
732768
if temp_dir_to_clean:

agent_starter_pack/cli/utils/template.py

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import os
1818
import pathlib
1919
import shutil
20+
import subprocess
2021
import sys
2122
import tempfile
2223
from dataclasses import dataclass
@@ -25,7 +26,7 @@
2526
import yaml
2627
from cookiecutter.main import cookiecutter
2728
from rich.console import Console
28-
from rich.prompt import IntPrompt, Prompt
29+
from rich.prompt import Confirm, IntPrompt, Prompt
2930

3031
from agent_starter_pack.cli.utils.version import get_current_version
3132

@@ -36,6 +37,105 @@
3637
)
3738

3839

40+
def add_base_template_dependencies_interactively(
41+
project_path: pathlib.Path,
42+
base_dependencies: list[str],
43+
base_template_name: str,
44+
auto_approve: bool = False,
45+
) -> bool:
46+
"""Interactively add base template dependencies using uv add.
47+
48+
Args:
49+
project_path: Path to the project directory
50+
base_dependencies: List of dependencies from base template's extra_dependencies
51+
base_template_name: Name of the base template being used
52+
auto_approve: Whether to skip confirmation and auto-install
53+
54+
Returns:
55+
True if dependencies were added successfully, False otherwise
56+
"""
57+
if not base_dependencies:
58+
return True
59+
60+
console = Console()
61+
62+
# Construct dependency string once for reuse
63+
deps_str = " ".join(f"'{dep}'" for dep in base_dependencies)
64+
65+
# Show what dependencies will be added
66+
console.print(
67+
f"\n✓ Base template override: Using '{base_template_name}' as foundation",
68+
style="bold cyan",
69+
)
70+
console.print(" This requires adding the following dependencies:", style="white")
71+
for dep in base_dependencies:
72+
console.print(f" • {dep}", style="yellow")
73+
74+
# Ask for confirmation unless auto-approve
75+
should_add = True
76+
if not auto_approve:
77+
should_add = Confirm.ask(
78+
"\n? Add these dependencies automatically?", default=True
79+
)
80+
81+
if not should_add:
82+
console.print("\n⚠️ Skipped dependency installation.", style="yellow")
83+
console.print(" To add them manually later, run:", style="dim")
84+
console.print(f" cd {project_path.name}", style="dim")
85+
console.print(f" uv add {deps_str}\n", style="dim")
86+
return False
87+
88+
# Run uv add
89+
try:
90+
if auto_approve:
91+
console.print(
92+
f"✓ Auto-installing dependencies: {', '.join(base_dependencies)}",
93+
style="bold cyan",
94+
)
95+
else:
96+
console.print(f"\n✓ Running: uv add {deps_str}", style="bold cyan")
97+
98+
# Run uv add in the project directory
99+
cmd = ["uv", "add"] + base_dependencies
100+
result = subprocess.run(
101+
cmd,
102+
cwd=project_path,
103+
capture_output=True,
104+
text=True,
105+
check=True,
106+
)
107+
108+
# Show success message
109+
if not auto_approve:
110+
# Show a summary line from uv output
111+
output_lines = result.stderr.strip().split("\n")
112+
for line in output_lines:
113+
if "Resolved" in line or "Installed" in line:
114+
console.print(f" {line}", style="dim")
115+
break
116+
117+
console.print("✓ Dependencies added successfully\n", style="bold green")
118+
return True
119+
120+
except subprocess.CalledProcessError as e:
121+
console.print(
122+
f"\n✗ Failed to add dependencies: {e.stderr.strip()}", style="bold red"
123+
)
124+
console.print(" You can add them manually:", style="yellow")
125+
console.print(f" cd {project_path.name}", style="dim")
126+
console.print(f" uv add {deps_str}\n", style="dim")
127+
return False
128+
except FileNotFoundError:
129+
console.print(
130+
"\n✗ uv command not found. Please install uv first.", style="bold red"
131+
)
132+
console.print(" Install from: https://docs.astral.sh/uv/", style="dim")
133+
console.print("\n To add dependencies manually:", style="yellow")
134+
console.print(f" cd {project_path.name}", style="dim")
135+
console.print(f" uv add {deps_str}\n", style="dim")
136+
return False
137+
138+
39139
def validate_agent_directory_name(agent_dir: str) -> None:
40140
"""Validate that an agent directory name is a valid Python identifier.
41141

docs/cli/create.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,30 @@ Output directory for the project (default: current directory)
8181
### `--agent-directory`, `-dir` DIRECTORY
8282
Name of the agent directory (overrides template default, usually `app`). This determines where your agent code files will be located within the project structure.
8383

84+
### `--base-template`, `-bt` TEMPLATE
85+
Override the base template specified in a remote template's configuration. Only applicable when using remote templates with the `--agent` flag. This allows you to use a different built-in agent as the foundation instead of the one specified in the remote template's `pyproject.toml`.
86+
87+
**Interactive dependency management:**
88+
When overriding the base template, you'll be prompted to add required dependencies using `uv add`:
89+
90+
```bash
91+
# Use adk_a2a_base as base instead of the remote template's default
92+
uvx agent-starter-pack create my-agent -a github.com/user/template --base-template adk_a2a_base
93+
94+
✓ Base template override: Using 'adk_a2a_base' as foundation
95+
This requires adding the following dependencies:
96+
• google-adk>=1.16.0,<2.0.0
97+
• a2a-sdk~=0.3.9
98+
99+
? Add these dependencies automatically? [Y/n]
100+
```
101+
102+
With `--auto-approve`, dependencies are added automatically without prompting:
103+
```bash
104+
uvx agent-starter-pack create my-agent -a template --base-template adk_a2a_base --auto-approve
105+
# Dependencies are added automatically
106+
```
107+
84108
### `--in-folder`
85109
Create agent files directly in the current directory instead of creating a new project subdirectory.
86110

@@ -138,8 +162,11 @@ uvx agent-starter-pack create my-agent -a https://github.com/user/my-template
138162
# Use shorthand notation with branch
139163
uvx agent-starter-pack create my-agent -a github.com/user/template@develop
140164

141-
# Use your existing project
165+
# Use your existing project
142166
uvx agent-starter-pack create my-agent -a local@./my-project
167+
168+
# Override the base template of a remote template
169+
uvx agent-starter-pack create my-agent -a adk@data-science --base-template adk_live
143170
```
144171

145172
### Advanced Configuration

docs/remote-templates/creating-remote-templates.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ description = "An awesome AI agent template"
146146
dependencies = ["google-adk>=1.8.0", "custom-lib"]
147147

148148
[tool.agent-starter-pack]
149+
# Base template to inherit from - users can override with --base-template flag
149150
base_template = "adk_base"
150151
name = "My Awesome Template" # Optional: falls back to [project].name
151152
description = "Custom description" # Optional: falls back to [project].description
@@ -157,6 +158,8 @@ frontend_type = "adk_streamlit"
157158
agent_directory = "app"
158159
```
159160

161+
**Note:** Users can override the `base_template` when creating from your template using `--base-template`. When they do, the CLI will automatically prompt them to add any additional dependencies required by the new base template using `uv add`.
162+
160163
## Configuration Reference
161164

162165
### Configuration Fallback Behavior

docs/remote-templates/using-remote-templates.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,69 @@ uvx agent-starter-pack create my-agent -a template-url --include-data-ingestion
116116
# Custom session storage
117117
uvx agent-starter-pack create my-agent -a template-url --session-type alloydb
118118

119+
# Override the base template
120+
uvx agent-starter-pack create my-agent -a template-url --base-template adk_live
121+
119122
# Skip verification checks
120123
uvx agent-starter-pack create my-agent -a template-url --skip-checks
121124
```
122125

126+
### Overriding Base Templates
127+
128+
Remote templates can specify a base template in their `pyproject.toml` configuration. You can override this using the `--base-template` flag to use a different foundational agent:
129+
130+
```bash
131+
# Use adk_a2a_base as the base instead of what the template specifies
132+
uvx agent-starter-pack create my-agent -a adk@data-science --base-template adk_a2a_base
133+
134+
✓ Base template override: Using 'adk_a2a_base' as foundation
135+
This requires adding the following dependencies:
136+
• google-adk>=1.16.0,<2.0.0
137+
• a2a-sdk~=0.3.9
138+
139+
? Add these dependencies automatically? [Y/n] y
140+
141+
✓ Running: uv add 'google-adk>=1.16.0,<2.0.0' 'a2a-sdk~=0.3.9'
142+
Resolved 111 packages in 1.2s
143+
✓ Dependencies added successfully
144+
```
145+
146+
#### Interactive Dependency Management
147+
148+
When you override the base template, the CLI:
149+
1. **Shows required dependencies** - Lists all dependencies needed by the new base template
150+
2. **Prompts for confirmation** - Asks if you want to add them automatically
151+
3. **Runs `uv add`** - Uses standard `uv` commands to add dependencies with proper version resolution
152+
4. **Handles conflicts** - `uv` automatically resolves any version conflicts between remote template and base dependencies
153+
154+
**Skipping dependency installation:**
155+
```bash
156+
? Add these dependencies automatically? [Y/n] n
157+
158+
⚠️ Skipped dependency installation.
159+
To add them manually later, run:
160+
cd my-agent
161+
uv add 'google-adk>=1.16.0,<2.0.0' 'a2a-sdk~=0.3.9'
162+
```
163+
164+
**Automatic installation with `--auto-approve`:**
165+
```bash
166+
uvx agent-starter-pack create my-agent -a template --base-template adk_a2a_base --auto-approve
167+
168+
✓ Base template override: Using 'adk_a2a_base' as foundation
169+
✓ Auto-installing dependencies: google-adk>=1.16.0,<2.0.0, a2a-sdk~=0.3.9
170+
Resolved 111 packages in 1.2s
171+
✓ Dependencies added successfully
172+
```
173+
174+
#### Use Cases
175+
176+
Base template override is useful when:
177+
- You want to use a remote template's logic with a different foundational agent
178+
- The template's default base doesn't match your deployment needs (e.g., switching from Cloud Run to Agent Engine)
179+
- You're experimenting with different base agents for the same custom logic
180+
- You need features from a different base (e.g., A2A protocol support via `adk_a2a_base`)
181+
123182
## Discovering Templates
124183

125184
### List Available Templates

0 commit comments

Comments
 (0)