Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions src/conductor/cli/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,24 @@ def run(
),
),
] = False,
workspace_instructions: Annotated[
bool,
typer.Option(
"--workspace-instructions",
help=(
"Auto-discover workspace instruction files "
"(AGENTS.md, CLAUDE.md, .github/copilot-instructions.md) "
"and prepend them to all agent prompts."
),
),
] = False,
raw_instructions: Annotated[
list[str] | None,
typer.Option(
"--instructions",
help="Path to instruction file(s) to prepend to all agent prompts. Can be repeated.",
),
] = None,
) -> None:
"""Run a workflow from a YAML file.

Expand All @@ -329,6 +347,8 @@ def run(
conductor run workflow.yaml --web
conductor run workflow.yaml --web --web-port 8080
conductor run workflow.yaml --web-bg
conductor run workflow.yaml --workspace-instructions
conductor run workflow.yaml --instructions AGENTS.md
"""
import asyncio
import json
Expand Down Expand Up @@ -418,6 +438,8 @@ def run(
no_interactive=True, # Always non-interactive in background
web_port=web_port,
metadata=cli_metadata,
workspace_instructions=workspace_instructions,
cli_instructions=raw_instructions,
)
console.print(f"[bold cyan]Dashboard:[/bold cyan] {url}")
console.print(
Expand All @@ -443,6 +465,8 @@ def run(
web_port=web_port,
web_bg=web_bg,
metadata=cli_metadata,
workspace_instructions=workspace_instructions,
cli_instructions=raw_instructions,
)
)

Expand Down
11 changes: 11 additions & 0 deletions src/conductor/cli/bg_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ def launch_background(
no_interactive: bool = True,
web_port: int = 0,
metadata: dict[str, str] | None = None,
workspace_instructions: bool = False,
cli_instructions: list[str] | None = None,
) -> str:
"""Fork a detached child process running the workflow with a web dashboard.

Expand All @@ -79,6 +81,8 @@ def launch_background(
no_interactive: Whether to disable interactive mode (always True for bg).
web_port: Desired port (0 = auto-select).
metadata: Optional CLI metadata key=value pairs.
workspace_instructions: Whether to auto-discover workspace instruction files.
cli_instructions: Optional list of instruction file paths.

Returns:
The dashboard URL (e.g. ``http://127.0.0.1:8080``).
Expand Down Expand Up @@ -123,6 +127,13 @@ def launch_background(
if log_file:
cmd.extend(["--log-file", str(log_file)])

if workspace_instructions:
cmd.append("--workspace-instructions")

if cli_instructions:
for instr_path in cli_instructions:
cmd.extend(["--instructions", instr_path])

# Launch detached child
kwargs: dict[str, Any] = {
"stdout": subprocess.DEVNULL,
Expand Down
22 changes: 22 additions & 0 deletions src/conductor/cli/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -1026,6 +1026,8 @@ async def run_workflow_async(
web_port: int = 0,
web_bg: bool = False,
metadata: dict[str, str] | None = None,
workspace_instructions: bool = False,
cli_instructions: list[str] | None = None,
) -> dict[str, Any]:
"""Execute a workflow asynchronously.

Expand All @@ -1040,6 +1042,8 @@ async def run_workflow_async(
web_port: Port for the web dashboard (0 = auto-select).
web_bg: If True, auto-shutdown dashboard after workflow + client disconnect.
metadata: Optional CLI metadata to merge on top of YAML-declared metadata.
workspace_instructions: If True, auto-discover workspace instruction files.
cli_instructions: Optional list of instruction file paths from CLI.

Returns:
The workflow output as a dictionary.
Expand Down Expand Up @@ -1118,6 +1122,22 @@ async def run_workflow_async(
verbose_log(f"Provider override: {provider_override}", style="yellow")
config.workflow.runtime.provider = provider_override # type: ignore[assignment]

# Build workspace instructions preamble
instructions_preamble: str | None = None
if workspace_instructions or cli_instructions or config.workflow.instructions:
from conductor.config.instructions import build_instructions_preamble

instructions_preamble = build_instructions_preamble(
auto_discover_dir=Path.cwd() if workspace_instructions else None,
yaml_instructions=config.workflow.instructions or None,
cli_instruction_paths=cli_instructions,
)
if instructions_preamble:
verbose_log(
f"Workspace instructions loaded ({len(instructions_preamble)} chars)",
style="cyan",
)

# Convert MCP servers from workflow config to SDK format
mcp_servers = await _build_mcp_servers(config)

Expand Down Expand Up @@ -1159,6 +1179,7 @@ async def run_workflow_async(
event_emitter=emitter,
keyboard_listener=listener,
web_dashboard=dashboard,
instructions_preamble=instructions_preamble,
run_context=RunContext(
run_id=event_log_subscriber.run_id if event_log_subscriber else "",
log_file=str(event_log_subscriber.path) if event_log_subscriber else "",
Expand Down Expand Up @@ -1583,6 +1604,7 @@ async def resume_workflow_async(
workflow_path=resolved_workflow_path,
interrupt_event=interrupt_event,
keyboard_listener=listener,
instructions_preamble=cp.instructions_preamble,
)
engine.set_context(restored_context)
engine.set_limits(restored_limits)
Expand Down
Loading
Loading