Skip to content

Commit dcab5ad

Browse files
523034406523034406
authored andcommitted
feat: A/B/C/D response logic for skill proposal
Plugin changes: - SessionTracker: add awaiting_choice flag - cmd_propose: set awaiting_choice=True, inject clear AI instructions - cmd_generate: parse A/B/C/D shorthand + --type md|py|both flag - Selective file generation (md only, py only, or both) - D choice → skip with message - Response shows type_label (SKILL.md / plugin.py / both) SKILL.md changes: - Add 'Handling User Response' table mapping A/B/C/D to commands - Explicit 'Do NOT generate until user responds' instruction Test: 173 passed
1 parent 325359b commit dcab5ad

3 files changed

Lines changed: 86 additions & 12 deletions

File tree

2.61 KB
Binary file not shown.

plugins/soloflow.py

Lines changed: 73 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -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"

skills/meta/soloflow/SKILL.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,19 @@ Reply with A, B, C, or D (or just "yes" for C).
139139

140140
Only propose one skill at a time. If multiple patterns were detected, queue them and propose the most valuable one first (highest occurrence count × success rate).
141141

142+
### Handling User Response
143+
144+
When the user replies with A, B, C, D, or "yes", run the corresponding command:
145+
146+
| User replies | Command to run |
147+
|---|---|
148+
| `A` | `/soloflow generate --type md` |
149+
| `B` | `/soloflow generate --type py` |
150+
| `C` or `yes` | `/soloflow generate` (both files, recommended) |
151+
| `D` | Say "Skipped. Run `/soloflow propose` when ready." |
152+
153+
**Important:** Do NOT generate anything until the user explicitly responds. Wait for their choice.
154+
142155
---
143156

144157
## Phase 4: Skill Generation

0 commit comments

Comments
 (0)