Skip to content
Open
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
22 changes: 16 additions & 6 deletions src/ai_assistant/logic/agent/agent_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@

console = Console()

async def run_knight_mode(session, goal: str):
async def run_agentic_workflow(session, goal: str, interactive: bool):
"""
The main entry point for the agentic mode.
Orchestrates the planning and execution phases.
Orchestrates the planning and execution phases for any agentic task.
"""
if not goal:
return console.print("[red]Usage: /knight <your project goal>[/red]")
console.print("[red]Agentic goal cannot be empty.[/red]")
return

planner = Planner(session)
executor = Executor(session)
Expand All @@ -24,5 +24,15 @@ async def run_knight_mode(session, goal: str):
console.print(Panel("Could not formulate a valid plan. Aborting.", border_style=Theme.ERROR, title=f"[{Theme.ERROR}]Planning Failed[/{Theme.ERROR}]"))
return

# Pass the goal to the executor for a better summary
await executor.execute_plan(plan, goal)
# Pass the goal and interactive flag to the executor
await executor.execute_plan(plan, goal, interactive=interactive)

async def run_knight_mode(session, goal: str):
"""
The main entry point for the explicit '/knight' agentic mode.
Invokes the agentic workflow in full interactive mode.
"""
if not goal:
return console.print("[red]Usage: /knight <your project goal>[/red]")

await run_agentic_workflow(session, goal, interactive=True)
57 changes: 31 additions & 26 deletions src/ai_assistant/logic/agent/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,17 +69,18 @@ def _render_step_for_display(self, step: dict[str, Any]) -> Tuple[str, str]:

return action_text, reasoning

async def execute_plan(self, plan: List[Any], goal: str) -> None:
summary = await self._summarize_plan_with_ai(plan, goal)

summary_title = Text("Execution Summary", style=Theme.SUMMARY_TITLE)
console.print(Panel(Markdown(summary), title=summary_title, border_style=Theme.SUMMARY_BORDER, title_align="left"))
async def execute_plan(self, plan: List[Any], goal: str, interactive: bool = True) -> None:
if interactive:
summary = await self._summarize_plan_with_ai(plan, goal)

summary_title = Text("Execution Summary", style=Theme.SUMMARY_TITLE)
console.print(Panel(Markdown(summary), title=summary_title, border_style=Theme.SUMMARY_BORDER, title_align="left"))

if not await questionary.confirm("Proceed with this plan?", default=True, auto_enter=False).ask_async():
console.print("[yellow]Plan execution aborted by user.[/yellow]")
return
if not await questionary.confirm("Proceed with this plan?", default=True, auto_enter=False).ask_async():
console.print("[yellow]Plan execution aborted by user.[/yellow]")
return

console.print()
console.print()

editable_plan = copy.deepcopy(plan)

Expand All @@ -97,24 +98,28 @@ async def execute_plan(self, plan: List[Any], goal: str) -> None:
break

current_step += 1
step_title_text = Text(f"Step {current_step}/{total_steps}", style="")

# --- RENDER THE ABSTRACTED VIEW ---
action_str, reasoning_str = self._render_step_for_display(step)
display_content = f"{action_str}\n\n[bold]Reasoning:[/bold] [dim]{reasoning_str}[/dim]"
console.print(Panel(display_content, title=step_title_text, border_style=Theme.STEP_PANEL_BORDER, expand=False))

action = await questionary.select("Action:", choices=["Execute", "Skip", "Edit", "Abort"]).ask_async()

if action == "Abort": return
if action == "Skip": continue
if action == "Edit":
step_json = json.dumps(step, indent=2)
edited_json_str = await questionary.text("Edit step JSON:", multiline=True, default=step_json).ask_async()
try:
step = json.loads(edited_json_str or "{}")
command_name = step.get("command") # Re-read command name after edit
except json.JSONDecodeError: continue

if interactive:
step_title_text = Text(f"Step {current_step}/{total_steps}", style="")
display_content = f"{action_str}\n\n[bold]Reasoning:[/bold] [dim]{reasoning_str}[/dim]"
console.print(Panel(display_content, title=step_title_text, border_style=Theme.STEP_PANEL_BORDER, expand=False))

action = await questionary.select("Action:", choices=["Execute", "Skip", "Edit", "Abort"]).ask_async()

if action == "Abort": return
if action == "Skip": continue
if action == "Edit":
step_json = json.dumps(step, indent=2)
edited_json_str = await questionary.text("Edit step JSON:", multiline=True, default=step_json).ask_async()
try:
step = json.loads(edited_json_str or "{}")
command_name = step.get("command") # Re-read command name after edit
except json.JSONDecodeError: continue
else:
# Non-interactive mode: just announce the step being executed.
panel_title = Text(f"Step {current_step}/{total_steps}", style=Theme.PROMPT)
console.print(Panel(action_str, title=panel_title, border_style=Theme.STEP_PANEL_BORDER, expand=False))

args = step.get("arguments", {})
if command_name in self.tools:
Expand Down