@@ -230,15 +230,29 @@ def add_base_template_dependencies_interactively(
230230 return False
231231
232232
233- def validate_agent_directory_name (agent_dir : str ) -> None :
233+ def validate_agent_directory_name (agent_dir : str , allow_dot : bool = False ) -> None :
234234 """Validate that an agent directory name is a valid Python identifier.
235235
236236 Args:
237237 agent_dir: The agent directory name to validate
238+ allow_dot: If True, allows "." as a special value indicating flat structure
238239
239240 Raises:
240241 ValueError: If the agent directory name is not a valid Python identifier
242+
243+ Note:
244+ The special value "." indicates flat structure - agent code is in the
245+ template root. When "." is used, the target directory name will be
246+ derived from the template folder name.
241247 """
248+ if agent_dir == "." :
249+ if allow_dot :
250+ return # "." is valid when explicitly allowed (will be resolved later)
251+ raise ValueError (
252+ "Agent directory '.' is not valid in this context. "
253+ "Use '.' only to indicate flat structure templates."
254+ )
255+
242256 if "-" in agent_dir :
243257 raise ValueError (
244258 f"Agent directory '{ agent_dir } ' contains hyphens (-) which are not allowed. "
@@ -879,7 +893,11 @@ def process_template(
879893 def get_agent_directory (
880894 template_config : dict [str , Any ], cli_overrides : dict [str , Any ] | None = None
881895 ) -> str :
882- """Get agent directory with CLI override support."""
896+ """Get agent directory with CLI override support.
897+
898+ Handles the special case where agent_directory is "." (flat structure),
899+ deriving the target directory name from the remote template folder name.
900+ """
883901 agent_dir = None
884902 if (
885903 cli_overrides
@@ -892,6 +910,20 @@ def get_agent_directory(
892910 "agent_directory" , "app"
893911 )
894912
913+ # Handle "." (flat structure) - derive target from folder name
914+ if agent_dir == "." :
915+ if remote_template_path :
916+ # Derive from remote template folder name
917+ folder_name = remote_template_path .name .replace ("-" , "_" )
918+ logging .debug (
919+ f"Flat structure (-dir .): deriving target '{ folder_name } ' from folder name"
920+ )
921+ agent_dir = folder_name
922+ else :
923+ # Fallback to "app" for non-remote templates
924+ logging .debug ("Flat structure (-dir .): using 'app' as fallback" )
925+ agent_dir = "app"
926+
895927 # Validate agent directory is a valid Python identifier
896928 validate_agent_directory_name (agent_dir )
897929
@@ -1239,13 +1271,39 @@ def get_agent_directory(
12391271 f"Preserved base template { preserve_file } as starter_pack_{ base_name } { extension } "
12401272 )
12411273
1242- copy_files (
1243- remote_template_path ,
1244- generated_project_dir ,
1245- agent_name = agent_name ,
1246- overwrite = True ,
1247- agent_directory = agent_directory ,
1274+ # Check if this is a flat structure template
1275+ # Flat structure can be detected via:
1276+ # 1. Auto-detection (is_flat_structure flag in remote_config)
1277+ # 2. source_agent_directory set to "." in config
1278+ # 3. CLI override with -dir . (agent_directory = ".")
1279+ cli_agent_dir = (
1280+ cli_overrides .get ("settings" , {}).get ("agent_directory" )
1281+ if cli_overrides
1282+ else None
1283+ )
1284+ is_flat_structure = (cli_agent_dir == "." ) or (
1285+ remote_config and remote_config .get ("is_flat_structure" , False )
12481286 )
1287+
1288+ if is_flat_structure :
1289+ # For flat structures, Python files go to agent_directory
1290+ logging .debug (
1291+ f"Flat structure detected: copying files to { agent_directory } /"
1292+ )
1293+ copy_flat_structure_agent_files (
1294+ remote_template_path ,
1295+ generated_project_dir ,
1296+ agent_directory ,
1297+ )
1298+ else :
1299+ # Standard structure: copy as-is
1300+ copy_files (
1301+ remote_template_path ,
1302+ generated_project_dir ,
1303+ agent_name = agent_name ,
1304+ overwrite = True ,
1305+ agent_directory = agent_directory ,
1306+ )
12491307 logging .debug ("Remote template files copied successfully" )
12501308
12511309 # Handle ADK agent compatibility
@@ -1738,3 +1796,54 @@ def copy_deployment_files(
17381796 )
17391797 else :
17401798 logging .warning (f"Deployment target directory not found: { deployment_path } " )
1799+
1800+
1801+ def copy_flat_structure_agent_files (
1802+ src : pathlib .Path ,
1803+ dst : pathlib .Path ,
1804+ agent_directory : str ,
1805+ ) -> None :
1806+ """Copy agent files from a flat structure template to the agent directory.
1807+
1808+ For flat structure templates, Python files (*.py) in the root are copied
1809+ to the agent directory, while other files are copied to the project root.
1810+
1811+ Args:
1812+ src: Source path (template root with flat structure)
1813+ dst: Destination path (project root)
1814+ agent_directory: Target agent directory name
1815+ """
1816+ agent_dst = dst / agent_directory
1817+ agent_dst .mkdir (parents = True , exist_ok = True )
1818+
1819+ # Files that should go to agent directory
1820+ agent_file_extensions = {".py" }
1821+ # Files to skip entirely
1822+ skip_files = {"pyproject.toml" , "uv.lock" , "README.md" , ".gitignore" }
1823+
1824+ for item in src .iterdir ():
1825+ if item .name .startswith ("." ) or item .name in skip_files :
1826+ continue
1827+ if item .name == "__pycache__" :
1828+ continue
1829+
1830+ if item .is_file ():
1831+ if item .suffix in agent_file_extensions :
1832+ # Python files go to agent directory
1833+ dest_file = agent_dst / item .name
1834+ logging .debug (
1835+ f"Flat structure: copying { item .name } -> { agent_directory } /{ item .name } "
1836+ )
1837+ shutil .copy2 (item , dest_file )
1838+ else :
1839+ # Other files go to project root
1840+ dest_file = dst / item .name
1841+ logging .debug (f"Flat structure: copying { item .name } -> { item .name } " )
1842+ shutil .copy2 (item , dest_file )
1843+ elif item .is_dir ():
1844+ # Directories are copied to project root (preserving structure)
1845+ dest_dir = dst / item .name
1846+ logging .debug (f"Flat structure: copying directory { item .name } " )
1847+ if dest_dir .exists ():
1848+ shutil .rmtree (dest_dir )
1849+ shutil .copytree (item , dest_dir )
0 commit comments