From 33b6956f596f1a3f25277900ba5770c49ae1e368 Mon Sep 17 00:00:00 2001 From: "didericis (claude)" Date: Mon, 29 Jun 2026 12:54:58 -0400 Subject: [PATCH] feat: headless_prompt method on AgentProvider + --prompt arg --- bot_bottle/cli/start.py | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/bot_bottle/cli/start.py b/bot_bottle/cli/start.py index 2966035..d1e539c 100644 --- a/bot_bottle/cli/start.py +++ b/bot_bottle/cli/start.py @@ -21,7 +21,7 @@ import tempfile from pathlib import Path from typing import Callable -from ..agent_provider import runtime_for +from ..agent_provider import get_provider, runtime_for from ..backend import ( Bottle, BottleSpec, @@ -83,6 +83,11 @@ def cmd_start(argv: list[str]) -> int: default=None, help="bottle color, one of the 16 ANSI color names (--headless default: none)", ) + parser.add_argument( + "--prompt", + default=None, + help="initial task prompt delivered to the agent (required with --headless)", + ) parser.add_argument( "name", nargs="?", @@ -169,6 +174,13 @@ def _start_headless( die("--headless requires an agent name: ./cli.py start --headless") manifest.require_agent(agent_name) # raises ManifestError if unknown + prompt = args.prompt + if not prompt: + die( + "--headless requires --prompt: " + "./cli.py start --headless --prompt 'Do the thing'" + ) + if args.bottle: bottle_names: tuple[str, ...] = tuple(args.bottle) else: @@ -192,7 +204,11 @@ def _start_headless( bottle_names=bottle_names, ) return _launch_bottle( - spec, dry_run=dry_run, backend_name=backend_name, assume_yes=True + spec, + dry_run=dry_run, + backend_name=backend_name, + assume_yes=True, + headless_prompt_text=prompt, ) @@ -480,13 +496,18 @@ def _launch_bottle( dry_run: bool, backend_name: str | None = None, assume_yes: bool = False, + headless_prompt_text: str = "", ) -> int: """Shared launch core for `start` and `resume`. Builds the plan, prints / dry-runs / prompts as appropriate, brings the bottle up, attaches claude, and prints the resume hint on session end. `assume_yes` skips the interactive y/N confirmation (headless / - orchestrator launches), where there is no human at the prompt.""" + orchestrator launches), where there is no human at the prompt. + + `headless_prompt_text` is passed to the provider's `headless_prompt` + method and the resulting args are appended to startup_args so the + agent receives the initial task without interactive input.""" stage_dir = Path(tempfile.mkdtemp(prefix="bot-bottle-stage.")) identity = "" try: @@ -504,10 +525,17 @@ def _launch_bottle( backend = get_bottle_backend(backend_name) with backend.launch(plan) as bottle: agent_provider_template = getattr(plan, "agent_provider_template", "claude") + extra_args: tuple[str, ...] = () + if headless_prompt_text: + extra_args = tuple( + get_provider(agent_provider_template).headless_prompt( + headless_prompt_text + ) + ) exit_code = attach_agent( bottle, agent_provider_template=agent_provider_template, - startup_args=plan.agent_provision.startup_args, + startup_args=plan.agent_provision.startup_args + extra_args, ) info( f"session ended (exit {exit_code}); "