feat(cron-recipes): /cron-recipe <name> seeds a conversational fill
Reworks the chat-line UX: pick a recipe by name and the agent asks you for
what it needs, one question at a time, instead of forcing you to hand-type a
slot=val command line.
- /cron-recipe -> lists the catalog
- /cron-recipe <name> -> forgiving name match (exact/prefix/substring/
fuzzy; ambiguous lists candidates), then seeds
the agent with a natural-language fill request
built from the recipe's typed slots + schedule
and prompt templates. The agent asks for each
value one at a time and calls the EXISTING
cronjob tool. No new tool.
- /cron-recipe <name> slot=val -> unchanged deterministic path (fill_recipe ->
create_job) for the dashboard/docs/power user.
Mechanism (no new plumbing, invariant-safe — the seed enters as a normal user
turn, never a synthetic injection):
- shared handler returns RecipeCommandResult{text, agent_seed}; match_recipe()
and build_recipe_seed() are the new shared pieces.
- gateway: dispatch rewrites event.text to the seed and falls through to the
agent (the same pattern /steer uses).
- CLI: handler sets a one-shot self._pending_agent_seed; the interactive loop
consumes it right after process_command() and runs it as the next turn.
The typed-slot schema stays the single source of truth (still validates the
form/inline path via fill_recipe); the agent path just renders those slots into
the questions to ask. Docs updated to lead with the name-then-ask flow.
This commit is contained in:
@@ -1279,10 +1279,12 @@ class CLICommandsMixin:
|
||||
def _handle_cron_recipe_command(self, cmd: str):
|
||||
"""Handle /cron-recipe — set up an automation from a recipe template.
|
||||
|
||||
Delegates to the shared handler so CLI, TUI, and gateway never drift.
|
||||
The user pastes a pre-filled command (from the docs/dashboard or a bare
|
||||
``/cron-recipe`` listing), edits the slot values, and sends; the handler
|
||||
validates and creates the cron job, or names the slot that's missing.
|
||||
Delegates to the shared handler. A bare ``/cron-recipe`` lists the
|
||||
catalog; ``/cron-recipe <name>`` name-matches a recipe and seeds the
|
||||
agent to ask the user for each value conversationally (the result's
|
||||
``agent_seed``); ``/cron-recipe <name> slot=val …`` creates the job
|
||||
directly. When a seed is returned it is stashed as a one-shot pending
|
||||
message the interactive loop runs as the next agent turn.
|
||||
"""
|
||||
import shlex
|
||||
|
||||
@@ -1293,10 +1295,16 @@ class CLICommandsMixin:
|
||||
args = " ".join(shlex.quote(t) for t in tokens)
|
||||
try:
|
||||
from hermes_cli.cron_recipe_cmd import handle_cron_recipe_command
|
||||
output = handle_cron_recipe_command(args)
|
||||
result = handle_cron_recipe_command(args)
|
||||
except Exception as e:
|
||||
output = f"Cron recipe command failed: {e}"
|
||||
self._console_print(output)
|
||||
self._console_print(f"Cron recipe command failed: {e}")
|
||||
return
|
||||
self._console_print(result.text)
|
||||
seed = getattr(result, "agent_seed", None)
|
||||
if seed:
|
||||
# One-shot: the interactive loop picks this up right after the
|
||||
# slash command returns and runs it as a normal agent turn.
|
||||
self._pending_agent_seed = seed
|
||||
|
||||
def _handle_curator_command(self, cmd: str):
|
||||
"""Handle /curator slash command.
|
||||
|
||||
Reference in New Issue
Block a user