@@ -201,6 +201,7 @@ def __init__(self):
201201 self .proposal_queue : list [dict [str , Any ]] = []
202202 self .last_proposal : dict [str , Any ] | None = None
203203 self .last_generated_skill : Skill | None = None
204+ self .awaiting_choice : bool = False
204205
205206 self .session_start = time .time ()
206207 self .tool_calls_count = 0
@@ -233,6 +234,7 @@ def clear(self):
233234 self .proposal_queue .clear ()
234235 self .last_proposal = None
235236 self .last_generated_skill = None
237+ self .awaiting_choice = False
236238 self .tool_calls_count = 0
237239 self .commands_count = 0
238240 self .workflows_recorded = 0
@@ -492,35 +494,76 @@ async def cmd_propose(ctx, args: str = ""):
492494
493495 await ctx .reply (proposal_text )
494496
497+ _tracker .awaiting_choice = True
498+
495499 await ctx .inject_system_message (
496500 "The user has triggered /soloflow propose. "
497- "A skill proposal has been presented. "
498- "Wait for the user's response (A/B/C/D or 'yes'). "
499- "When they respond, use the appropriate action to generate the skill."
501+ "A skill proposal has been presented with options A/B/C/D. "
502+ "WAIT for the user's response. "
503+ "When they reply with A, B, C, D, or 'yes':\n "
504+ "- A → run `/soloflow generate --type md`\n "
505+ "- B → run `/soloflow generate --type py`\n "
506+ "- C or 'yes' → run `/soloflow generate` (both files, recommended)\n "
507+ "- D → say 'Skipped. Run `/soloflow propose` when ready.'\n "
508+ "Do NOT generate anything until the user responds."
500509 )
501510
502511 # ------------------------------------------------------------------
503- # /soloflow generate < name>
512+ # /soloflow generate [ name] [--type md|py|both]
504513 # ------------------------------------------------------------------
505514 @hermes .command (
506515 name = "soloflow generate" ,
507516 description = "Generate and install a skill from the last proposal" ,
508- usage = "/soloflow generate [skill-name]" ,
517+ usage = "/soloflow generate [skill-name] [--type md|py|both] " ,
509518 )
510519 async def cmd_generate (ctx , args : str = "" ):
511- """Generate and install a skill from the last proposal."""
520+ """Generate and install a skill from the last proposal.
521+
522+ Supports A/B/C/D shorthand from proposal response:
523+ A → --type md (SKILL.md only)
524+ B → --type py (plugin.py only)
525+ C or no flag → --type both (recommended)
526+ D → skip
527+ """
512528 if not _tracker .last_proposal :
513529 await ctx .reply (
514530 "No skill proposal active. Run `/soloflow propose` first."
515531 )
516532 return
517533
534+ # Parse args: extract --type and skill name
535+ generate_type = "both" # default
536+ skill_name = None
537+
538+ args_parts = args .strip ().split () if args .strip () else []
539+ i = 0
540+ while i < len (args_parts ):
541+ if args_parts [i ] == "--type" and i + 1 < len (args_parts ):
542+ generate_type = args_parts [i + 1 ].lower ()
543+ i += 2
544+ elif args_parts [i ].upper () in ("A" , "B" , "C" , "D" ):
545+ # A/B/C/D shorthand
546+ choice = args_parts [i ].upper ()
547+ if choice == "A" :
548+ generate_type = "md"
549+ elif choice == "B" :
550+ generate_type = "py"
551+ elif choice == "C" :
552+ generate_type = "both"
553+ elif choice == "D" :
554+ _tracker .awaiting_choice = False
555+ await ctx .reply ("⏭️ Skipped. Run `/soloflow propose` when ready." )
556+ return
557+ i += 1
558+ else :
559+ skill_name = args_parts [i ]
560+ i += 1
561+
562+ _tracker .awaiting_choice = False
563+
518564 pattern = _tracker .last_proposal ["pattern" ]
519565 score = _tracker .last_proposal ["score" ]
520566
521- # Use provided name or generate from pattern
522- skill_name = args .strip () if args .strip () else None
523-
524567 # Package the skill
525568 skill = _tracker .packager .package_pattern (
526569 pattern ,
@@ -532,8 +575,26 @@ async def cmd_generate(ctx, args: str = ""):
532575 skill .name = skill_name .lower ().replace (" " , "-" ).replace ("_" , "-" )
533576 skill .display_name = skill_name .replace ("-" , " " ).replace ("_" , " " ).title ()
534577
535- # Install to Hermes directory
536- installed_files = _tracker .packager .install_skill (skill )
578+ # Install to Hermes directory (selective by type)
579+ installed_files = []
580+
581+ hermes_dir = Path .home () / ".hermes"
582+
583+ if generate_type in ("md" , "both" ):
584+ skill_dir = hermes_dir / "skills" / skill .category / skill .name
585+ skill_dir .mkdir (parents = True , exist_ok = True )
586+ skill_md_path = skill_dir / "SKILL.md"
587+ skill_md_path .write_text (skill .skill_md_content , encoding = "utf-8" )
588+ installed_files .append (skill_md_path )
589+
590+ if generate_type in ("py" , "both" ):
591+ plugins_dir = hermes_dir / "plugins"
592+ plugins_dir .mkdir (parents = True , exist_ok = True )
593+ plugin_py_path = plugins_dir / f"{ skill .name } .py"
594+ plugin_py_path .write_text (skill .plugin_py_content , encoding = "utf-8" )
595+ installed_files .append (plugin_py_path )
596+
597+ type_label = {"md" : "SKILL.md" , "py" : "plugin.py" , "both" : "SKILL.md + plugin.py" }[generate_type ]
537598
538599 # Update skill quality scores
539600 skill .quality_score = score .overall_score
@@ -557,7 +618,7 @@ async def cmd_generate(ctx, args: str = ""):
557618 files_list = "\n " .join (f"- `{ f } `" for f in installed_files )
558619
559620 await ctx .reply (
560- f"✅ **Skill '{ skill .display_name } ' generated and installed !**\n \n "
621+ f"✅ **Skill '{ skill .display_name } ' generated!** ( { type_label } ) \n \n "
561622 f"Files written:\n { files_list } \n \n "
562623 f"Quality Score: { score .overall_score :.2f} (Grade: { score .grade } )\n \n "
563624 f"Next steps:\n "
0 commit comments