diff --git a/assets/prompts/step_resolution.hbs b/assets/prompts/step_resolution.hbs index 74bc43e078..523584dafc 100644 --- a/assets/prompts/step_resolution.hbs +++ b/assets/prompts/step_resolution.hbs @@ -1,22 +1,27 @@ -Your task is to map a step from the conversation above to suggestions on symbols inside the provided source files. + +Your task is to map a step from a workflow to locations in source code where code needs to be changed to fulfill that step. +Given a workflow containing background context plus a series of tags, you will resolve *one* of these step tags to resolve to one or more locations in the code. +With each location, you will produce a brief, one-line description of the changes to be made. -Guidelines: + - There's no need to describe *what* to do, just *where* to do it. +- Only reference locations that actually exist (unless you're creating a file). - If creating a file, assume any subsequent updates are included at the time of creation. -- Don't create and then update a file. -- We'll create it in one shot. +- Don't create and then update a file. Always create new files in shot. - Prefer updating symbols lower in the syntax tree if possible. - Never include suggestions on a parent symbol and one of its children in the same suggestions block. - Never nest an operation with another operation or include CDATA or other content. All suggestions are leaf nodes. -- Include a description attribute for each operation with a brief, one-line description of the change to perform. - Descriptions are required for all suggestions except delete. - When generating multiple suggestions, ensure the descriptions are specific to each individual operation. - Avoid referring to the location in the description. Focus on the change to be made, not the location where it's made. That's implicit with the symbol you provide. - Don't generate multiple suggestions at the same location. Instead, combine them together in a single operation with a succinct combined description. + + -Example 1: - -User: + + + + ```rs src/rectangle.rs struct Rectangle { width: f64, @@ -30,12 +35,21 @@ impl Rectangle { } ``` +We need to add methods to calculate the area and perimeter of the rectangle. Can you help with that? + + +Sure, I can help with that! + Add new methods 'calculate_area' and 'calculate_perimeter' to the Rectangle struct Implement the 'Display' trait for the Rectangle struct + + -What are the suggestions for the step: Add a new method 'calculate_area' to the Rectangle struct + +Add new methods 'calculate_area' and 'calculate_perimeter' to the Rectangle struct + -A (wrong): + { "title": "Add Rectangle methods", "suggestions": [ @@ -53,10 +67,9 @@ A (wrong): } ] } + -This demonstrates what NOT to do. NEVER append multiple children at the same location. - -A (corrected): + { "title": "Add Rectangle methods", "suggestions": [ @@ -68,11 +81,13 @@ A (corrected): } ] } + -User: -What are the suggestions for the step: Implement the 'Display' trait for the Rectangle struct + +Implement the 'Display' trait for the Rectangle struct + -A: + { "title": "Implement Display for Rectangle", "suggestions": [ @@ -84,10 +99,11 @@ A: } ] } + -Example 2: - -User: + + + ```rs src/user.rs struct User { pub name: String, @@ -105,13 +121,19 @@ impl User { } } ``` - + + +Certainly! Update the 'print_info' method to use formatted output Remove the 'email' field from the User struct + + -What are the suggestions for the step: Update the 'print_info' method to use formatted output + +Update the 'print_info' method to use formatted output + -A: + { "title": "Use formatted output", "suggestions": [ @@ -123,11 +145,13 @@ A: } ] } + -User: -What are the suggestions for the step: Remove the 'email' field from the User struct + +Remove the 'email' field from the User struct + -A: + { "title": "Remove email field", "suggestions": [ @@ -138,10 +162,12 @@ A: } ] } + + -Example 3: - -User: + + + ```rs src/vehicle.rs struct Vehicle { make: String, @@ -159,13 +185,18 @@ impl Vehicle { } } ``` - + + Add a 'use std::fmt;' statement at the beginning of the file Add a new method 'start_engine' in the Vehicle impl block + + -What are the suggestions for the step: Add a 'use std::fmt;' statement at the beginning of the file + +Add a 'use std::fmt;' statement at the beginning of the file + -A: + { "title": "Add use std::fmt statement", "suggestions": [ @@ -176,11 +207,13 @@ A: } ] } + -User: -What are the suggestions for the step: Add a new method 'start_engine' in the Vehicle impl block + +Add a new method 'start_engine' in the Vehicle impl block + -A: + { "title": "Add start_engine method", "suggestions": [ @@ -192,10 +225,12 @@ A: } ] } + + -Example 4: - -User: + + + ```rs src/employee.rs struct Employee { name: String, @@ -219,12 +254,18 @@ impl Employee { } } ``` - + + Make salary an f32 +Remove the 'department' field and update the 'print_details' method + + -What are the suggestions for the step: Make salary an f32 + +Make salary an f32 + -A (wrong): + { "title": "Change salary to f32", "suggestions": [ @@ -242,10 +283,9 @@ A (wrong): } ] } + -This example demonstrates what not to do. `struct Employee salary` is a child of `struct Employee`. - -A (corrected): + { "title": "Change salary to f32", "suggestions": [ @@ -257,11 +297,13 @@ A (corrected): } ] } + -User: -What are the correct suggestions for the step: Remove the 'department' field and update the 'print_details' method + +Remove the 'department' field and update the 'print_details' method + -A: + { "title": "Remove department", "suggestions": [ @@ -278,10 +320,12 @@ A: } ] } + + -Example 5: - -User: + + + ```rs src/game.rs struct Player { name: String, @@ -305,10 +349,17 @@ impl Game { } } ``` - + + Add a 'level' field to Player and update the 'new' method + + -A: + +Add a 'level' field to Player and update the 'new' method + + + { "title": "Add level field to Player", "suggestions": [ @@ -326,10 +377,12 @@ A: } ] } + + -Example 6: - -User: + + + ```rs src/config.rs use std::collections::HashMap; @@ -343,10 +396,17 @@ impl Config { } } ``` - + + Add a 'load_from_file' method to Config and import necessary modules + + -A: + +Add a 'load_from_file' method to Config and import necessary modules + + + { "title": "Add load_from_file method", "suggestions": [ @@ -363,10 +423,12 @@ A: } ] } + + -Example 7: - -User: + + + ```rs src/database.rs pub(crate) struct Database { connection: Connection, @@ -383,10 +445,17 @@ impl Database { } } ``` - + + Add error handling to the 'query' method and create a custom error type + + -A: + +Add error handling to the 'query' method and create a custom error type + + + { "title": "Add error handling to query", "suggestions": [ @@ -409,5 +478,16 @@ A: } ] } + + + Now generate the suggestions for the following step: + + +{{{workflow_context}}} + + + +{{{step_to_resolve}}} + diff --git a/crates/assistant/src/assistant_panel.rs b/crates/assistant/src/assistant_panel.rs index fc1d77b81b..ef840aa1c8 100644 --- a/crates/assistant/src/assistant_panel.rs +++ b/crates/assistant/src/assistant_panel.rs @@ -2911,7 +2911,7 @@ impl ContextEditor { let mut assist_ids = Vec::new(); for (excerpt_id, suggestion_group) in suggestion_groups { for suggestion in &suggestion_group.suggestions { - assist_ids.extend(suggestion.show( + assist_ids.extend(suggestion.kind.show( &editor, excerpt_id, workspace, diff --git a/crates/assistant/src/context.rs b/crates/assistant/src/context.rs index 4d41f54a2c..f144f6c96a 100644 --- a/crates/assistant/src/context.rs +++ b/crates/assistant/src/context.rs @@ -2974,12 +2974,12 @@ mod tests { model .as_fake() - .respond_to_last_tool_use(tool::WorkflowStepResolution { + .respond_to_last_tool_use(tool::WorkflowStepResolutionTool { step_title: "Title".into(), - suggestions: vec![tool::WorkflowSuggestion { + suggestions: vec![tool::WorkflowSuggestionTool { path: "/root/hello.rs".into(), // Simulate a symbol name that's slightly different than our outline query - kind: tool::WorkflowSuggestionKind::Update { + kind: tool::WorkflowSuggestionToolKind::Update { symbol: "fn main()".into(), description: "Extract a greeting function".into(), }, diff --git a/crates/assistant/src/context_inspector.rs b/crates/assistant/src/context_inspector.rs index 340411bbbe..eb79557955 100644 --- a/crates/assistant/src/context_inspector.rs +++ b/crates/assistant/src/context_inspector.rs @@ -11,7 +11,7 @@ use ui::{ div, h_flex, px, Color, Element as _, ParentElement as _, Styled, ViewContext, WindowContext, }; -use crate::{Context, ResolvedWorkflowStep, WorkflowSuggestion}; +use crate::{Context, ResolvedWorkflowStep, WorkflowSuggestion, WorkflowSuggestionKind}; type StepRange = Range; @@ -68,7 +68,7 @@ impl ContextInspector { .and_then(|file| file.path().to_str()) .unwrap_or("untitled"); let snapshot = buffer.text_snapshot(); - writeln!(output, " {buffer_path}:").ok()?; + writeln!(output, "Path: {buffer_path}:").ok()?; for group in suggestion_groups { for suggestion in &group.suggestions { pretty_print_workflow_suggestion(&mut output, suggestion, &snapshot); @@ -163,7 +163,7 @@ impl ContextInspector { } fn pretty_print_anchor( out: &mut String, - anchor: &language::Anchor, + anchor: language::Anchor, snapshot: &text::BufferSnapshot, ) { use std::fmt::Write; @@ -177,9 +177,9 @@ fn pretty_print_range( ) { use std::fmt::Write; write!(out, " Range: ").ok(); - pretty_print_anchor(out, &range.start, snapshot); + pretty_print_anchor(out, range.start, snapshot); write!(out, "..").ok(); - pretty_print_anchor(out, &range.end, snapshot); + pretty_print_anchor(out, range.end, snapshot); } fn pretty_print_workflow_suggestion( @@ -188,37 +188,46 @@ fn pretty_print_workflow_suggestion( snapshot: &text::BufferSnapshot, ) { use std::fmt::Write; - let (range, description, position) = match suggestion { - WorkflowSuggestion::Update { range, description } => (Some(range), Some(description), None), - WorkflowSuggestion::CreateFile { description } => (None, Some(description), None), - WorkflowSuggestion::AppendChild { - position, - description, + let (position, description, range) = match &suggestion.kind { + WorkflowSuggestionKind::Update { range, description } => { + (None, Some(description), Some(range)) } - | WorkflowSuggestion::InsertSiblingBefore { + WorkflowSuggestionKind::CreateFile { description } => (None, Some(description), None), + WorkflowSuggestionKind::AppendChild { position, description, - } - | WorkflowSuggestion::InsertSiblingAfter { + } => (Some(position), Some(description), None), + WorkflowSuggestionKind::InsertSiblingBefore { position, description, - } - | WorkflowSuggestion::PrependChild { + } => (Some(position), Some(description), None), + WorkflowSuggestionKind::InsertSiblingAfter { position, description, - } => (None, Some(description), Some(position)), - - WorkflowSuggestion::Delete { range } => (Some(range), None, None), + } => (Some(position), Some(description), None), + WorkflowSuggestionKind::PrependChild { + position, + description, + } => (Some(position), Some(description), None), + WorkflowSuggestionKind::Delete { range } => (None, None, Some(range)), }; + writeln!(out, " Tool input: {}", suggestion.tool_input).ok(); + writeln!( + out, + " Tool output: {}", + serde_json::to_string_pretty(&suggestion.tool_output) + .expect("Should not fail on valid struct serialization") + ) + .ok(); if let Some(description) = description { writeln!(out, " Description: {description}").ok(); } if let Some(range) = range { - pretty_print_range(out, range, snapshot); + pretty_print_range(out, &range, snapshot); } if let Some(position) = position { write!(out, " Position: ").ok(); - pretty_print_anchor(out, position, snapshot); + pretty_print_anchor(out, *position, snapshot); write!(out, "\n").ok(); } write!(out, "\n").ok(); diff --git a/crates/assistant/src/prompts.rs b/crates/assistant/src/prompts.rs index 4bbab19a9a..ddd4681a4b 100644 --- a/crates/assistant/src/prompts.rs +++ b/crates/assistant/src/prompts.rs @@ -31,6 +31,15 @@ pub struct TerminalAssistantPromptContext { pub user_prompt: String, } +/// Context required to generate a workflow step resolution prompt. +#[derive(Debug, Serialize)] +pub struct StepResolutionContext { + /// The full context, including ... tags + pub workflow_context: String, + /// The text of the specific step from the context to resolve + pub step_to_resolve: String, +} + pub struct PromptBuilder { handlebars: Arc>>, } @@ -278,7 +287,10 @@ impl PromptBuilder { self.handlebars.lock().render("edit_workflow", &()) } - pub fn generate_step_resolution_prompt(&self) -> Result { - self.handlebars.lock().render("step_resolution", &()) + pub fn generate_step_resolution_prompt( + &self, + context: &StepResolutionContext, + ) -> Result { + self.handlebars.lock().render("step_resolution", context) } } diff --git a/crates/assistant/src/workflow.rs b/crates/assistant/src/workflow.rs index 5a208434c2..ecb0545c4d 100644 --- a/crates/assistant/src/workflow.rs +++ b/crates/assistant/src/workflow.rs @@ -1,4 +1,6 @@ -use crate::{AssistantPanel, Context, InlineAssistId, InlineAssistant}; +use crate::{ + prompts::StepResolutionContext, AssistantPanel, Context, InlineAssistId, InlineAssistant, +}; use anyhow::{anyhow, Error, Result}; use collections::HashMap; use editor::Editor; @@ -10,7 +12,7 @@ use project::Project; use rope::Point; use serde::{Deserialize, Serialize}; use smol::stream::StreamExt; -use std::{cmp, ops::Range, sync::Arc}; +use std::{cmp, fmt::Write, ops::Range, sync::Arc}; use text::{AnchorRangeExt as _, OffsetRangeExt as _}; use util::ResultExt as _; use workspace::Workspace; @@ -34,7 +36,36 @@ pub struct WorkflowSuggestionGroup { } #[derive(Clone, Debug, Eq, PartialEq)] -pub enum WorkflowSuggestion { +pub struct WorkflowSuggestion { + pub kind: WorkflowSuggestionKind, + pub tool_input: String, + pub tool_output: tool::WorkflowSuggestionTool, +} + +impl WorkflowSuggestion { + pub fn range(&self) -> Range { + self.kind.range() + } + + pub fn show( + &self, + editor: &View, + excerpt_id: editor::ExcerptId, + workspace: &WeakView, + assistant_panel: &View, + cx: &mut ui::ViewContext, + ) -> Option { + self.kind + .show(editor, excerpt_id, workspace, assistant_panel, cx) + } + + fn try_merge(&mut self, other: &mut WorkflowSuggestion, snapshot: &BufferSnapshot) -> bool { + self.kind.try_merge(&other.kind, snapshot) + } +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum WorkflowSuggestionKind { Update { range: Range, description: String, @@ -87,6 +118,15 @@ impl WorkflowStepResolution { .text_for_range(self.tagged_range.clone()) .collect::(); + let mut workflow_context = String::new(); + for message in context.messages(cx) { + write!(&mut workflow_context, "", message.role).unwrap(); + for chunk in context_buffer.read(cx).text_for_range(message.offset_range) { + write!(&mut workflow_context, "{chunk}").unwrap(); + } + write!(&mut workflow_context, "").unwrap(); + } + Some(cx.spawn(|this, mut cx| async move { let result = async { let Some(model) = model else { @@ -99,7 +139,12 @@ impl WorkflowStepResolution { cx.notify(); })?; - let mut prompt = prompt_builder.generate_step_resolution_prompt()?; + let resolution_context = StepResolutionContext { + workflow_context, + step_to_resolve: step_text.clone(), + }; + let mut prompt = + prompt_builder.generate_step_resolution_prompt(&resolution_context)?; prompt.push_str(&step_text); request.messages.push(LanguageModelRequestMessage { role: Role::User, @@ -108,7 +153,7 @@ impl WorkflowStepResolution { // Invoke the model to get its edit suggestions for this workflow step. let mut stream = model - .use_tool_stream::(request, &cx) + .use_tool_stream::(request, &cx) .await?; while let Some(chunk) = stream.next().await { let chunk = chunk?; @@ -119,14 +164,16 @@ impl WorkflowStepResolution { } let resolution = this.update(&mut cx, |this, _| { - serde_json::from_str::(&this.output) + serde_json::from_str::(&this.output) })??; // Translate the parsed suggestions to our internal types, which anchor the suggestions to locations in the code. let suggestion_tasks: Vec<_> = resolution .suggestions .iter() - .map(|suggestion| suggestion.resolve(project.clone(), cx.clone())) + .map(|suggestion| { + suggestion.resolve(step_text.clone(), project.clone(), cx.clone()) + }) .collect(); // Expand the context ranges of each suggestion and group suggestions with overlapping context ranges. @@ -152,7 +199,7 @@ impl WorkflowStepResolution { suggestions.sort_by(|a, b| a.range().cmp(&b.range(), &snapshot)); // Merge overlapping suggestions - suggestions.dedup_by(|a, b| b.try_merge(&a, &snapshot)); + suggestions.dedup_by(|a, b| b.try_merge(a, &snapshot)); // Create context ranges for each suggestion for suggestion in suggestions { @@ -214,40 +261,40 @@ impl WorkflowStepResolution { } } -impl WorkflowSuggestion { +impl WorkflowSuggestionKind { pub fn range(&self) -> Range { match self { - WorkflowSuggestion::Update { range, .. } => range.clone(), - WorkflowSuggestion::CreateFile { .. } => language::Anchor::MIN..language::Anchor::MAX, - WorkflowSuggestion::InsertSiblingBefore { position, .. } - | WorkflowSuggestion::InsertSiblingAfter { position, .. } - | WorkflowSuggestion::PrependChild { position, .. } - | WorkflowSuggestion::AppendChild { position, .. } => *position..*position, - WorkflowSuggestion::Delete { range } => range.clone(), + Self::Update { range, .. } => range.clone(), + Self::CreateFile { .. } => language::Anchor::MIN..language::Anchor::MAX, + Self::InsertSiblingBefore { position, .. } + | Self::InsertSiblingAfter { position, .. } + | Self::PrependChild { position, .. } + | Self::AppendChild { position, .. } => *position..*position, + Self::Delete { range } => range.clone(), } } pub fn description(&self) -> Option<&str> { match self { - WorkflowSuggestion::Update { description, .. } - | WorkflowSuggestion::CreateFile { description } - | WorkflowSuggestion::InsertSiblingBefore { description, .. } - | WorkflowSuggestion::InsertSiblingAfter { description, .. } - | WorkflowSuggestion::PrependChild { description, .. } - | WorkflowSuggestion::AppendChild { description, .. } => Some(description), - WorkflowSuggestion::Delete { .. } => None, + Self::Update { description, .. } + | Self::CreateFile { description } + | Self::InsertSiblingBefore { description, .. } + | Self::InsertSiblingAfter { description, .. } + | Self::PrependChild { description, .. } + | Self::AppendChild { description, .. } => Some(description), + Self::Delete { .. } => None, } } fn description_mut(&mut self) -> Option<&mut String> { match self { - WorkflowSuggestion::Update { description, .. } - | WorkflowSuggestion::CreateFile { description } - | WorkflowSuggestion::InsertSiblingBefore { description, .. } - | WorkflowSuggestion::InsertSiblingAfter { description, .. } - | WorkflowSuggestion::PrependChild { description, .. } - | WorkflowSuggestion::AppendChild { description, .. } => Some(description), - WorkflowSuggestion::Delete { .. } => None, + Self::Update { description, .. } + | Self::CreateFile { description } + | Self::InsertSiblingBefore { description, .. } + | Self::InsertSiblingAfter { description, .. } + | Self::PrependChild { description, .. } + | Self::AppendChild { description, .. } => Some(description), + Self::Delete { .. } => None, } } @@ -286,16 +333,16 @@ impl WorkflowSuggestion { let snapshot = buffer.read(cx).snapshot(cx); match self { - WorkflowSuggestion::Update { range, description } => { + Self::Update { range, description } => { initial_prompt = description.clone(); suggestion_range = snapshot.anchor_in_excerpt(excerpt_id, range.start)? ..snapshot.anchor_in_excerpt(excerpt_id, range.end)?; } - WorkflowSuggestion::CreateFile { description } => { + Self::CreateFile { description } => { initial_prompt = description.clone(); suggestion_range = editor::Anchor::min()..editor::Anchor::min(); } - WorkflowSuggestion::InsertSiblingBefore { + Self::InsertSiblingBefore { position, description, } => { @@ -311,7 +358,7 @@ impl WorkflowSuggestion { line_start..line_start }); } - WorkflowSuggestion::InsertSiblingAfter { + Self::InsertSiblingAfter { position, description, } => { @@ -327,7 +374,7 @@ impl WorkflowSuggestion { line_start..line_start }); } - WorkflowSuggestion::PrependChild { + Self::PrependChild { position, description, } => { @@ -343,7 +390,7 @@ impl WorkflowSuggestion { line_start..line_start }); } - WorkflowSuggestion::AppendChild { + Self::AppendChild { position, description, } => { @@ -359,7 +406,7 @@ impl WorkflowSuggestion { line_start..line_start }); } - WorkflowSuggestion::Delete { range } => { + Self::Delete { range } => { initial_prompt = "Delete".to_string(); suggestion_range = snapshot.anchor_in_excerpt(excerpt_id, range.start)? ..snapshot.anchor_in_excerpt(excerpt_id, range.end)?; @@ -392,15 +439,15 @@ pub mod tool { use schemars::JsonSchema; #[derive(Debug, Serialize, Deserialize, JsonSchema)] - pub struct WorkflowStepResolution { + pub struct WorkflowStepResolutionTool { /// An extremely short title for the edit step represented by these operations. pub step_title: String, /// A sequence of operations to apply to the codebase. /// When multiple operations are required for a step, be sure to include multiple operations in this list. - pub suggestions: Vec, + pub suggestions: Vec, } - impl LanguageModelTool for WorkflowStepResolution { + impl LanguageModelTool for WorkflowStepResolutionTool { fn name() -> String { "edit".into() } @@ -429,16 +476,17 @@ pub mod tool { /// programmatic changes to source code. It provides a structured way to describe /// edits for features like refactoring tools or AI-assisted coding suggestions. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] - pub struct WorkflowSuggestion { + pub struct WorkflowSuggestionTool { /// The path to the file containing the relevant operation pub path: String, #[serde(flatten)] - pub kind: WorkflowSuggestionKind, + pub kind: WorkflowSuggestionToolKind, } - impl WorkflowSuggestion { + impl WorkflowSuggestionTool { pub(super) async fn resolve( &self, + tool_input: String, project: Model, mut cx: AsyncAppContext, ) -> Result<(Model, super::WorkflowSuggestion)> { @@ -475,9 +523,8 @@ pub mod tool { let snapshot = buffer.update(&mut cx, |buffer, _| buffer.snapshot())?; let outline = snapshot.outline(None).context("no outline for buffer")?; - let suggestion; - match kind { - WorkflowSuggestionKind::Update { + let kind = match kind { + WorkflowSuggestionToolKind::Update { symbol, description, } => { @@ -494,12 +541,12 @@ pub mod tool { snapshot.line_len(symbol.range.end.row), ); let range = snapshot.anchor_before(start)..snapshot.anchor_after(end); - suggestion = super::WorkflowSuggestion::Update { range, description }; + WorkflowSuggestionKind::Update { range, description } } - WorkflowSuggestionKind::Create { description } => { - suggestion = super::WorkflowSuggestion::CreateFile { description }; + WorkflowSuggestionToolKind::Create { description } => { + WorkflowSuggestionKind::CreateFile { description } } - WorkflowSuggestionKind::InsertSiblingBefore { + WorkflowSuggestionToolKind::InsertSiblingBefore { symbol, description, } => { @@ -514,12 +561,12 @@ pub mod tool { annotation_range.start }), ); - suggestion = super::WorkflowSuggestion::InsertSiblingBefore { + WorkflowSuggestionKind::InsertSiblingBefore { position, description, - }; + } } - WorkflowSuggestionKind::InsertSiblingAfter { + WorkflowSuggestionToolKind::InsertSiblingAfter { symbol, description, } => { @@ -528,12 +575,12 @@ pub mod tool { .with_context(|| format!("symbol not found: {:?}", symbol))? .to_point(&snapshot); let position = snapshot.anchor_after(symbol.range.end); - suggestion = super::WorkflowSuggestion::InsertSiblingAfter { + WorkflowSuggestionKind::InsertSiblingAfter { position, description, - }; + } } - WorkflowSuggestionKind::PrependChild { + WorkflowSuggestionToolKind::PrependChild { symbol, description, } => { @@ -548,18 +595,18 @@ pub mod tool { .body_range .map_or(symbol.range.start, |body_range| body_range.start), ); - suggestion = super::WorkflowSuggestion::PrependChild { + WorkflowSuggestionKind::PrependChild { position, description, - }; + } } else { - suggestion = super::WorkflowSuggestion::PrependChild { + WorkflowSuggestionKind::PrependChild { position: language::Anchor::MIN, description, - }; + } } } - WorkflowSuggestionKind::AppendChild { + WorkflowSuggestionToolKind::AppendChild { symbol, description, } => { @@ -574,18 +621,18 @@ pub mod tool { .body_range .map_or(symbol.range.end, |body_range| body_range.end), ); - suggestion = super::WorkflowSuggestion::AppendChild { + WorkflowSuggestionKind::AppendChild { position, description, - }; + } } else { - suggestion = super::WorkflowSuggestion::PrependChild { + WorkflowSuggestionKind::PrependChild { position: language::Anchor::MAX, description, - }; + } } } - WorkflowSuggestionKind::Delete { symbol } => { + WorkflowSuggestionToolKind::Delete { symbol } => { let symbol = outline .find_most_similar(&symbol) .with_context(|| format!("symbol not found: {:?}", symbol))? @@ -599,9 +646,15 @@ pub mod tool { snapshot.line_len(symbol.range.end.row), ); let range = snapshot.anchor_before(start)..snapshot.anchor_after(end); - suggestion = super::WorkflowSuggestion::Delete { range }; + WorkflowSuggestionKind::Delete { range } } - } + }; + + let suggestion = WorkflowSuggestion { + kind, + tool_output: self.clone(), + tool_input, + }; Ok((buffer, suggestion)) } @@ -609,7 +662,7 @@ pub mod tool { #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] #[serde(tag = "kind")] - pub enum WorkflowSuggestionKind { + pub enum WorkflowSuggestionToolKind { /// Rewrites the specified symbol entirely based on the given description. /// This operation completely replaces the existing symbol with new content. Update {