@@ -62,27 +62,36 @@ def render_distribution(params: dict[str, Any], slug: str, description: str) ->
6262 env_requires = params .get ("env_requires" ) or []
6363 if not isinstance (env_requires , list ):
6464 raise ValueError ("env_requires must be a list" )
65- manifest = {
65+ manifest : dict [ str , Any ] = {
6666 "name" : slug ,
6767 "version" : str (params .get ("version" ) or "0.1.0" ),
6868 "description" : description ,
6969 "hermes_requires" : str (params .get ("hermes_requires" ) or ">=0.12.0" ),
7070 "author" : str (params .get ("author" ) or "Hermes profile author" ),
7171 "license" : str (params .get ("license" ) or "MIT" ),
72- "env_requires" : env_requires ,
73- "distribution_owned" : [
74- "SOUL.md" ,
75- "config.yaml" ,
76- "mcp.json" ,
77- "skills/" ,
78- "templates/" ,
79- "scripts/" ,
80- "distribution.yaml" ,
81- "README.md" ,
82- "AGENTS.md" ,
83- ".env.EXAMPLE" ,
84- ],
8572 }
73+ template_source = params .get ("template_source" )
74+ if template_source :
75+ if not isinstance (template_source , dict ):
76+ raise ValueError ("template_source must be a mapping" )
77+ manifest ["template_source" ] = template_source
78+ manifest .update (
79+ {
80+ "env_requires" : env_requires ,
81+ "distribution_owned" : [
82+ "SOUL.md" ,
83+ "config.yaml" ,
84+ "mcp.json" ,
85+ "skills/" ,
86+ "templates/" ,
87+ "scripts/" ,
88+ "distribution.yaml" ,
89+ "README.md" ,
90+ "AGENTS.md" ,
91+ ".env.EXAMPLE" ,
92+ ],
93+ }
94+ )
8695 return yaml .safe_dump (manifest , sort_keys = False , default_flow_style = False )
8796
8897
@@ -127,7 +136,6 @@ def render_config(params: dict[str, Any]) -> str:
127136 "memory" : {"memory_enabled" : True , "user_profile_enabled" : True },
128137 "security" : {"redact_secrets" : True },
129138 "approvals" : {"mode" : "manual" },
130- "toolsets" : toolsets ,
131139 },
132140 sort_keys = False ,
133141 )
@@ -229,10 +237,16 @@ def render_agents(display_name: str) -> str:
229237
230238
231239def render_readme (params : dict [str , Any ], slug : str , display_name : str , description : str ) -> str :
240+ template_source = params .get ("template_source" )
241+ lineage = ""
242+ if isinstance (template_source , dict ) and template_source .get ("url" ):
243+ template_name = str (template_source .get ("name" ) or "profile template" )
244+ template_url = str (template_source ["url" ])
245+ lineage = f"\n Template lineage: built from [{ template_name } ]({ template_url } ).\n "
232246 return f"""# { display_name }
233247
234248{ description }
235-
249+ { lineage }
236250This is a Hermes Agent profile distribution. It can be installed with `hermes profile install` and updated from git.
237251
238252## Install
@@ -274,6 +288,22 @@ def render_readme(params: dict[str, Any], slug: str, display_name: str, descript
274288"""
275289
276290
291+ def render_template_source_file (params : dict [str , Any ]) -> str | None :
292+ template_source = params .get ("template_source" )
293+ if not template_source :
294+ return None
295+ if not isinstance (template_source , dict ):
296+ raise ValueError ("template_source must be a mapping" )
297+ data = {
298+ "template" : template_source ,
299+ "notes" : [
300+ "GitHub native generated-from-template linkage is only available when a repository is created through GitHub's template flow." ,
301+ "This file records template lineage explicitly for humans and automation." ,
302+ ],
303+ }
304+ return yaml .safe_dump (data , sort_keys = False , default_flow_style = False )
305+
306+
277307def render_params_example (slug : str , display_name : str , description : str , author : str ) -> str :
278308 data = {
279309 "name" : slug ,
@@ -284,6 +314,11 @@ def render_params_example(slug: str, display_name: str, description: str, author
284314 "license" : "MIT" ,
285315 "model_provider" : "openrouter" ,
286316 "model_default" : "anthropic/claude-sonnet-4" ,
317+ "template_source" : {
318+ "name" : "codegraphtheory/hermes-profile-template" ,
319+ "url" : "https://github.com/codegraphtheory/hermes-profile-template" ,
320+ "relationship" : "generated-from-template" ,
321+ },
287322 "toolsets" : ["file" , "terminal" , "skills" , "web" , "session_search" , "clarify" ],
288323 "env_requires" : [],
289324 "principles" : [
@@ -343,6 +378,9 @@ def generate(params: dict[str, Any], output: Path, force: bool, template_root: P
343378 write (output / "mcp.json" , "{\n \" mcpServers\" : {}\n }" )
344379 write (output / "templates" / "profile.params.yaml" , render_params_example (slug , display_name , description , author ))
345380 copy_support_files (template_root , output )
381+ template_source_file = render_template_source_file (params )
382+ if template_source_file :
383+ write (output / ".github" / "template-source.yml" , template_source_file )
346384
347385 result = subprocess .run (
348386 ["python3" , str (output / "scripts" / "validate_profile.py" ), str (output )],
0 commit comments