add clippy warning for string_slice (#5422)
Co-authored-by: Douwe Osinga <douwe@squareup.com>
This commit is contained in:
@@ -12,6 +12,7 @@ description = "An AI agent"
|
|||||||
|
|
||||||
[workspace.lints.clippy]
|
[workspace.lints.clippy]
|
||||||
uninlined_format_args = "allow"
|
uninlined_format_args = "allow"
|
||||||
|
string_slice = "warn"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
rmcp = { version = "0.8.3", features = ["schemars", "auth"] }
|
rmcp = { version = "0.8.3", features = ["schemars", "auth"] }
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ impl GooseCompleter {
|
|||||||
/// Complete prompt names for the /prompt command
|
/// Complete prompt names for the /prompt command
|
||||||
fn complete_prompt_names(&self, line: &str) -> Result<(usize, Vec<Pair>)> {
|
fn complete_prompt_names(&self, line: &str) -> Result<(usize, Vec<Pair>)> {
|
||||||
// Get the prefix of the prompt name being typed
|
// Get the prefix of the prompt name being typed
|
||||||
let prefix = if line.len() > 8 { &line[8..] } else { "" };
|
let prefix = line.get(8..).unwrap_or("");
|
||||||
|
|
||||||
// Get available prompts from cache
|
// Get available prompts from cache
|
||||||
let cache = self.completion_cache.read().unwrap();
|
let cache = self.completion_cache.read().unwrap();
|
||||||
@@ -156,7 +156,7 @@ impl GooseCompleter {
|
|||||||
|
|
||||||
/// Complete argument keys for a specific prompt
|
/// Complete argument keys for a specific prompt
|
||||||
fn complete_argument_keys(&self, line: &str) -> Result<(usize, Vec<Pair>)> {
|
fn complete_argument_keys(&self, line: &str) -> Result<(usize, Vec<Pair>)> {
|
||||||
let parts: Vec<&str> = line[8..].split_whitespace().collect();
|
let parts: Vec<&str> = line.get(8..).unwrap_or("").split_whitespace().collect();
|
||||||
|
|
||||||
// We need at least the prompt name
|
// We need at least the prompt name
|
||||||
if parts.is_empty() {
|
if parts.is_empty() {
|
||||||
|
|||||||
@@ -169,15 +169,17 @@ fn handle_slash_command(input: &str) -> Option<InputResult> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
s if s.starts_with(CMD_EXTENSION) => Some(InputResult::AddExtension(
|
s if s.starts_with(CMD_EXTENSION) => Some(InputResult::AddExtension(
|
||||||
s[CMD_EXTENSION.len()..].to_string(),
|
s.get(CMD_EXTENSION.len()..).unwrap_or("").to_string(),
|
||||||
)),
|
)),
|
||||||
s if s.starts_with(CMD_BUILTIN) => {
|
s if s.starts_with(CMD_BUILTIN) => Some(InputResult::AddBuiltin(
|
||||||
Some(InputResult::AddBuiltin(s[CMD_BUILTIN.len()..].to_string()))
|
s.get(CMD_BUILTIN.len()..).unwrap_or("").to_string(),
|
||||||
|
)),
|
||||||
|
s if s.starts_with(CMD_MODE) => Some(InputResult::GooseMode(
|
||||||
|
s.get(CMD_MODE.len()..).unwrap_or("").to_string(),
|
||||||
|
)),
|
||||||
|
s if s.starts_with(CMD_PLAN) => {
|
||||||
|
parse_plan_command(s.get(CMD_PLAN.len()..).unwrap_or("").trim().to_string())
|
||||||
}
|
}
|
||||||
s if s.starts_with(CMD_MODE) => {
|
|
||||||
Some(InputResult::GooseMode(s[CMD_MODE.len()..].to_string()))
|
|
||||||
}
|
|
||||||
s if s.starts_with(CMD_PLAN) => parse_plan_command(s[CMD_PLAN.len()..].trim().to_string()),
|
|
||||||
s if s == CMD_ENDPLAN => Some(InputResult::EndPlan),
|
s if s == CMD_ENDPLAN => Some(InputResult::EndPlan),
|
||||||
s if s == CMD_CLEAR => Some(InputResult::Clear),
|
s if s == CMD_CLEAR => Some(InputResult::Clear),
|
||||||
s if s.starts_with(CMD_RECIPE) => parse_recipe_command(s),
|
s if s.starts_with(CMD_RECIPE) => parse_recipe_command(s),
|
||||||
@@ -199,7 +201,7 @@ fn parse_recipe_command(s: &str) -> Option<InputResult> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extract the filepath from the command
|
// Extract the filepath from the command
|
||||||
let filepath = s[CMD_RECIPE.len()..].trim();
|
let filepath = s.get(CMD_RECIPE.len()..).unwrap_or("").trim();
|
||||||
|
|
||||||
if filepath.is_empty() {
|
if filepath.is_empty() {
|
||||||
return Some(InputResult::Recipe(None));
|
return Some(InputResult::Recipe(None));
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
use rmcp::model::{Content, Role};
|
|
||||||
use std::collections::{HashMap, HashSet};
|
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
|
|
||||||
use crate::developer::analyze::types::{
|
use crate::developer::analyze::types::{
|
||||||
AnalysisMode, AnalysisResult, CallChain, EntryType, FocusedAnalysisData,
|
AnalysisMode, AnalysisResult, CallChain, EntryType, FocusedAnalysisData,
|
||||||
};
|
};
|
||||||
use crate::developer::lang;
|
use crate::developer::lang;
|
||||||
|
use goose::utils::safe_truncate;
|
||||||
|
use rmcp::model::{Content, Role};
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
pub struct Formatter;
|
pub struct Formatter;
|
||||||
|
|
||||||
@@ -164,13 +164,7 @@ impl Formatter {
|
|||||||
if imports.len() > 1 {
|
if imports.len() > 1 {
|
||||||
format!("{}({})", group, imports.len())
|
format!("{}({})", group, imports.len())
|
||||||
} else {
|
} else {
|
||||||
// For single imports, show more detail
|
safe_truncate(&imports[0], 40)
|
||||||
let imp = &imports[0];
|
|
||||||
if imp.len() > 40 {
|
|
||||||
format!("{}...", &imp[..37])
|
|
||||||
} else {
|
|
||||||
imp.clone()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
@@ -727,7 +721,7 @@ impl Formatter {
|
|||||||
if let Some(header_line) = output
|
if let Some(header_line) = output
|
||||||
.lines()
|
.lines()
|
||||||
.rev()
|
.rev()
|
||||||
.find(|l| l.starts_with("##") && line.contains(&l[3..]))
|
.find(|l| l.starts_with("##") && l.get(3..).is_some_and(|s| line.contains(s)))
|
||||||
{
|
{
|
||||||
if !filtered.contains(header_line) {
|
if !filtered.contains(header_line) {
|
||||||
filtered.push_str(header_line);
|
filtered.push_str(header_line);
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ pub fn find_method_for_receiver(
|
|||||||
for i in 0..parent.child_count() {
|
for i in 0..parent.child_count() {
|
||||||
if let Some(child) = parent.child(i) {
|
if let Some(child) = parent.child(i) {
|
||||||
if child.kind() == "field_identifier" {
|
if child.kind() == "field_identifier" {
|
||||||
return Some(source[child.byte_range()].to_string());
|
return source.get(child.byte_range()).map(|s| s.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ fn find_method_in_body_with_depth(
|
|||||||
for j in 0..child.child_count() {
|
for j in 0..child.child_count() {
|
||||||
if let Some(name_node) = child.child(j) {
|
if let Some(name_node) = child.child(j) {
|
||||||
if name_node.kind() == "identifier" {
|
if name_node.kind() == "identifier" {
|
||||||
return Some(source[name_node.byte_range()].to_string());
|
return source.get(name_node.byte_range()).map(|s| s.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,7 +83,9 @@ pub fn extract_function_name_for_kind(
|
|||||||
for i in 0..node.child_count() {
|
for i in 0..node.child_count() {
|
||||||
if let Some(child) = node.child(i) {
|
if let Some(child) = node.child(i) {
|
||||||
if child.kind() == "type_identifier" {
|
if child.kind() == "type_identifier" {
|
||||||
return Some(format!("impl {}", &source[child.byte_range()]));
|
return source
|
||||||
|
.get(child.byte_range())
|
||||||
|
.map(|s| format!("impl {}", s));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,7 +111,7 @@ pub fn find_method_for_receiver(
|
|||||||
for i in 0..parent.child_count() {
|
for i in 0..parent.child_count() {
|
||||||
if let Some(child) = parent.child(i) {
|
if let Some(child) = parent.child(i) {
|
||||||
if child.kind() == "identifier" {
|
if child.kind() == "identifier" {
|
||||||
return Some(source[child.byte_range()].to_string());
|
return source.get(child.byte_range()).map(|s| s.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -133,7 +135,7 @@ pub fn find_receiver_type(node: &tree_sitter::Node, source: &str) -> Option<Stri
|
|||||||
for i in 0..parent.child_count() {
|
for i in 0..parent.child_count() {
|
||||||
if let Some(child) = parent.child(i) {
|
if let Some(child) = parent.child(i) {
|
||||||
if child.kind() == "type_identifier" {
|
if child.kind() == "type_identifier" {
|
||||||
return Some(source[child.byte_range()].to_string());
|
return source.get(child.byte_range()).map(|s| s.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,7 +103,8 @@ impl ElementExtractor {
|
|||||||
source: &str,
|
source: &str,
|
||||||
kinds: &[&str],
|
kinds: &[&str],
|
||||||
) -> Option<String> {
|
) -> Option<String> {
|
||||||
Self::find_child_by_kind(node, kinds).map(|child| source[child.byte_range()].to_string())
|
Self::find_child_by_kind(node, kinds)
|
||||||
|
.and_then(|child| source.get(child.byte_range()).map(|s| s.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn extract_with_depth(
|
pub fn extract_with_depth(
|
||||||
@@ -216,8 +217,13 @@ impl ElementExtractor {
|
|||||||
for match_ in matches.by_ref() {
|
for match_ in matches.by_ref() {
|
||||||
for capture in match_.captures {
|
for capture in match_.captures {
|
||||||
let node = capture.node;
|
let node = capture.node;
|
||||||
let text = &source[node.byte_range()];
|
let Some(text) = source.get(node.byte_range()) else {
|
||||||
let line = source[..node.start_byte()].lines().count() + 1;
|
continue;
|
||||||
|
};
|
||||||
|
let line = source
|
||||||
|
.get(..node.start_byte())
|
||||||
|
.map(|s| s.lines().count() + 1)
|
||||||
|
.unwrap_or(1);
|
||||||
|
|
||||||
match query.capture_names()[capture.index as usize] {
|
match query.capture_names()[capture.index as usize] {
|
||||||
"func" | "const" => {
|
"func" | "const" => {
|
||||||
@@ -284,18 +290,25 @@ impl ElementExtractor {
|
|||||||
for match_ in matches.by_ref() {
|
for match_ in matches.by_ref() {
|
||||||
for capture in match_.captures {
|
for capture in match_.captures {
|
||||||
let node = capture.node;
|
let node = capture.node;
|
||||||
let text = &source[node.byte_range()];
|
let Some(text) = source.get(node.byte_range()) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
let start_pos = node.start_position();
|
let start_pos = node.start_position();
|
||||||
|
|
||||||
let line_start = source[..node.start_byte()]
|
let line_start = source
|
||||||
.rfind('\n')
|
.get(..node.start_byte())
|
||||||
|
.and_then(|s| s.rfind('\n'))
|
||||||
.map(|i| i + 1)
|
.map(|i| i + 1)
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
let line_end = source[node.end_byte()..]
|
let line_end = source
|
||||||
.find('\n')
|
.get(node.end_byte()..)
|
||||||
|
.and_then(|s| s.find('\n'))
|
||||||
.map(|i| node.end_byte() + i)
|
.map(|i| node.end_byte() + i)
|
||||||
.unwrap_or(source.len());
|
.unwrap_or(source.len());
|
||||||
let context = source[line_start..line_end].trim().to_string();
|
let context = source
|
||||||
|
.get(line_start..line_end)
|
||||||
|
.map(|s| s.trim().to_string())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
let caller_name = Self::find_containing_function(&node, source, language);
|
let caller_name = Self::find_containing_function(&node, source, language);
|
||||||
|
|
||||||
@@ -356,18 +369,25 @@ impl ElementExtractor {
|
|||||||
for match_ in matches.by_ref() {
|
for match_ in matches.by_ref() {
|
||||||
for capture in match_.captures {
|
for capture in match_.captures {
|
||||||
let node = capture.node;
|
let node = capture.node;
|
||||||
let text = &source[node.byte_range()];
|
let Some(text) = source.get(node.byte_range()) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
let start_pos = node.start_position();
|
let start_pos = node.start_position();
|
||||||
|
|
||||||
let line_start = source[..node.start_byte()]
|
let line_start = source
|
||||||
.rfind('\n')
|
.get(..node.start_byte())
|
||||||
|
.and_then(|s| s.rfind('\n'))
|
||||||
.map(|i| i + 1)
|
.map(|i| i + 1)
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
let line_end = source[node.end_byte()..]
|
let line_end = source
|
||||||
.find('\n')
|
.get(node.end_byte()..)
|
||||||
|
.and_then(|s| s.find('\n'))
|
||||||
.map(|i| node.end_byte() + i)
|
.map(|i| node.end_byte() + i)
|
||||||
.unwrap_or(source.len());
|
.unwrap_or(source.len());
|
||||||
let context = source[line_start..line_end].trim().to_string();
|
let context = source
|
||||||
|
.get(line_start..line_end)
|
||||||
|
.map(|s| s.trim().to_string())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
let capture_name = query.capture_names()[capture.index as usize];
|
let capture_name = query.capture_names()[capture.index as usize];
|
||||||
|
|
||||||
|
|||||||
@@ -28,8 +28,9 @@ impl MorphLLMEditor {
|
|||||||
if let (Some(start_pos), Some(end_pos)) = (text.find(&start_tag), text.find(&end_tag)) {
|
if let (Some(start_pos), Some(end_pos)) = (text.find(&start_tag), text.find(&end_tag)) {
|
||||||
if start_pos < end_pos {
|
if start_pos < end_pos {
|
||||||
let content_start = start_pos + start_tag.len();
|
let content_start = start_pos + start_tag.len();
|
||||||
let content = &text[content_start..end_pos];
|
if let Some(content) = text.get(content_start..end_pos) {
|
||||||
return Some(content.trim().to_string());
|
return Some(content.trim().to_string());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
|
|||||||
@@ -1335,20 +1335,22 @@ impl DeveloperServer {
|
|||||||
|
|
||||||
// Find the last space before AM/PM and replace it with U+202F
|
// Find the last space before AM/PM and replace it with U+202F
|
||||||
let space_pos = filename.rfind(meridian)
|
let space_pos = filename.rfind(meridian)
|
||||||
.map(|pos| filename[..pos].trim_end().len())
|
.and_then(|pos| filename.get(..pos).map(|s| s.trim_end().len()))
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
|
|
||||||
if space_pos > 0 {
|
if space_pos > 0 {
|
||||||
let parent = path.parent().unwrap_or(Path::new(""));
|
let parent = path.parent().unwrap_or(Path::new(""));
|
||||||
let new_filename = format!(
|
if let (Some(before), Some(after)) = (filename.get(..space_pos), filename.get(space_pos+1..)) {
|
||||||
"{}{}{}",
|
let new_filename = format!(
|
||||||
&filename[..space_pos],
|
"{}{}{}",
|
||||||
'\u{202F}',
|
before,
|
||||||
&filename[space_pos+1..]
|
'\u{202F}',
|
||||||
);
|
after
|
||||||
let new_path = parent.join(new_filename);
|
);
|
||||||
|
let new_path = parent.join(new_filename);
|
||||||
|
|
||||||
return new_path;
|
return new_path;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3208,7 +3210,10 @@ mod tests {
|
|||||||
) {
|
) {
|
||||||
let start_idx = start + start_tag.len();
|
let start_idx = start + start_tag.len();
|
||||||
if start_idx < end {
|
if start_idx < end {
|
||||||
let path = assistant_content.text[start_idx..end].trim();
|
let Some(path) = assistant_content.text.get(start_idx..end).map(|s| s.trim())
|
||||||
|
else {
|
||||||
|
panic!("Failed to extract path from assistant content");
|
||||||
|
};
|
||||||
println!("Extracted path: {}", path);
|
println!("Extracted path: {}", path);
|
||||||
|
|
||||||
let file_contents =
|
let file_contents =
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ struct LogEntry {
|
|||||||
fn parse_log_line(line: &str) -> Option<LogEntry> {
|
fn parse_log_line(line: &str) -> Option<LogEntry> {
|
||||||
line.find(": ").and_then(|pos| {
|
line.find(": ").and_then(|pos| {
|
||||||
let (prefix, content) = line.split_at(pos);
|
let (prefix, content) = line.split_at(pos);
|
||||||
let content = &content[2..]; // Skip ": "
|
let content = content.get(2..)?; // Skip ": "
|
||||||
|
|
||||||
let stream_type = match prefix {
|
let stream_type = match prefix {
|
||||||
"STDIN" => StreamType::Stdin,
|
"STDIN" => StreamType::Stdin,
|
||||||
|
|||||||
@@ -1365,17 +1365,9 @@ impl Agent {
|
|||||||
.unwrap_or(&content)
|
.unwrap_or(&content)
|
||||||
.trim()
|
.trim()
|
||||||
.to_string();
|
.to_string();
|
||||||
tracing::debug!(
|
|
||||||
"Cleaned content for parsing: {}",
|
|
||||||
&clean_content[..std::cmp::min(200, clean_content.len())]
|
|
||||||
);
|
|
||||||
|
|
||||||
// try to parse json response from the LLM
|
|
||||||
tracing::debug!("Attempting to parse recipe content as JSON");
|
|
||||||
let (instructions, activities) =
|
let (instructions, activities) =
|
||||||
if let Ok(json_content) = serde_json::from_str::<Value>(&clean_content) {
|
if let Ok(json_content) = serde_json::from_str::<Value>(&clean_content) {
|
||||||
tracing::debug!("Successfully parsed JSON content");
|
|
||||||
|
|
||||||
let instructions = json_content
|
let instructions = json_content
|
||||||
.get("instructions")
|
.get("instructions")
|
||||||
.ok_or_else(|| anyhow!("Missing 'instructions' in json response"))?
|
.ok_or_else(|| anyhow!("Missing 'instructions' in json response"))?
|
||||||
|
|||||||
@@ -249,7 +249,7 @@ fn extract_json_from_line(line: &str) -> Option<String> {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let potential_json = &line[start..=end];
|
let potential_json = line.get(start..=end)?;
|
||||||
if serde_json::from_str::<Value>(potential_json).is_ok() {
|
if serde_json::from_str::<Value>(potential_json).is_ok() {
|
||||||
Some(potential_json.to_string())
|
Some(potential_json.to_string())
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use tokio::process::Command;
|
|||||||
|
|
||||||
use super::base::{ConfigKey, Provider, ProviderMetadata, ProviderUsage, Usage};
|
use super::base::{ConfigKey, Provider, ProviderMetadata, ProviderUsage, Usage};
|
||||||
use super::errors::ProviderError;
|
use super::errors::ProviderError;
|
||||||
use super::utils::RequestLog;
|
use super::utils::{filter_extensions_from_system_prompt, RequestLog};
|
||||||
use crate::config::{Config, GooseMode};
|
use crate::config::{Config, GooseMode};
|
||||||
use crate::conversation::message::{Message, MessageContent};
|
use crate::conversation::message::{Message, MessageContent};
|
||||||
use crate::model::ModelConfig;
|
use crate::model::ModelConfig;
|
||||||
@@ -103,28 +103,6 @@ impl ClaudeCodeProvider {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Filter out the Extensions section from the system prompt
|
|
||||||
fn filter_extensions_from_system_prompt(&self, system: &str) -> String {
|
|
||||||
// Find the Extensions section and remove it
|
|
||||||
if let Some(extensions_start) = system.find("# Extensions") {
|
|
||||||
// Look for the next major section that starts with #
|
|
||||||
let after_extensions = &system[extensions_start..];
|
|
||||||
if let Some(next_section_pos) = after_extensions[1..].find("\n# ") {
|
|
||||||
// Found next section, keep everything before Extensions and after the next section
|
|
||||||
let before_extensions = &system[..extensions_start];
|
|
||||||
let next_section_start = extensions_start + next_section_pos + 1;
|
|
||||||
let after_next_section = &system[next_section_start..];
|
|
||||||
format!("{}{}", before_extensions.trim_end(), after_next_section)
|
|
||||||
} else {
|
|
||||||
// No next section found, just remove everything from Extensions onward
|
|
||||||
system[..extensions_start].trim_end().to_string()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// No Extensions section found, return original
|
|
||||||
system.to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert goose messages to the format expected by claude CLI
|
/// Convert goose messages to the format expected by claude CLI
|
||||||
fn messages_to_claude_format(&self, _system: &str, messages: &[Message]) -> Result<Value> {
|
fn messages_to_claude_format(&self, _system: &str, messages: &[Message]) -> Result<Value> {
|
||||||
let mut claude_messages = Vec::new();
|
let mut claude_messages = Vec::new();
|
||||||
@@ -303,8 +281,7 @@ impl ClaudeCodeProvider {
|
|||||||
ProviderError::RequestFailed(format!("Failed to format messages: {}", e))
|
ProviderError::RequestFailed(format!("Failed to format messages: {}", e))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// Create a filtered system prompt without Extensions section
|
let filtered_system = filter_extensions_from_system_prompt(system);
|
||||||
let filtered_system = self.filter_extensions_from_system_prompt(system);
|
|
||||||
|
|
||||||
if std::env::var("GOOSE_CLAUDE_CODE_DEBUG").is_ok() {
|
if std::env::var("GOOSE_CLAUDE_CODE_DEBUG").is_ok() {
|
||||||
println!("=== CLAUDE CODE PROVIDER DEBUG ===");
|
println!("=== CLAUDE CODE PROVIDER DEBUG ===");
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use tokio::process::Command;
|
|||||||
|
|
||||||
use super::base::{ConfigKey, Provider, ProviderMetadata, ProviderUsage, Usage};
|
use super::base::{ConfigKey, Provider, ProviderMetadata, ProviderUsage, Usage};
|
||||||
use super::errors::ProviderError;
|
use super::errors::ProviderError;
|
||||||
use super::utils::RequestLog;
|
use super::utils::{filter_extensions_from_system_prompt, RequestLog};
|
||||||
use crate::conversation::message::{Message, MessageContent};
|
use crate::conversation::message::{Message, MessageContent};
|
||||||
use crate::model::ModelConfig;
|
use crate::model::ModelConfig;
|
||||||
use rmcp::model::Tool;
|
use rmcp::model::Tool;
|
||||||
@@ -112,34 +112,11 @@ impl CursorAgentProvider {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Filter out the Extensions section from the system prompt
|
|
||||||
fn filter_extensions_from_system_prompt(&self, system: &str) -> String {
|
|
||||||
// Find the Extensions section and remove it
|
|
||||||
if let Some(extensions_start) = system.find("# Extensions") {
|
|
||||||
// Look for the next major section that starts with #
|
|
||||||
let after_extensions = &system[extensions_start..];
|
|
||||||
if let Some(next_section_pos) = after_extensions[1..].find("\n# ") {
|
|
||||||
// Found next section, keep everything before Extensions and after the next section
|
|
||||||
let before_extensions = &system[..extensions_start];
|
|
||||||
let next_section_start = extensions_start + next_section_pos + 1;
|
|
||||||
let after_next_section = &system[next_section_start..];
|
|
||||||
format!("{}{}", before_extensions.trim_end(), after_next_section)
|
|
||||||
} else {
|
|
||||||
// No next section found, just remove everything from Extensions onward
|
|
||||||
system[..extensions_start].trim_end().to_string()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// No Extensions section found, return original
|
|
||||||
system.to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert goose messages to a simple prompt format for cursor-agent CLI
|
/// Convert goose messages to a simple prompt format for cursor-agent CLI
|
||||||
fn messages_to_cursor_agent_format(&self, system: &str, messages: &[Message]) -> String {
|
fn messages_to_cursor_agent_format(&self, system: &str, messages: &[Message]) -> String {
|
||||||
let mut full_prompt = String::new();
|
let mut full_prompt = String::new();
|
||||||
|
|
||||||
// Add system prompt
|
let filtered_system = filter_extensions_from_system_prompt(system);
|
||||||
let filtered_system = self.filter_extensions_from_system_prompt(system);
|
|
||||||
full_prompt.push_str(&filtered_system);
|
full_prompt.push_str(&filtered_system);
|
||||||
full_prompt.push_str("\n\n");
|
full_prompt.push_str("\n\n");
|
||||||
|
|
||||||
@@ -267,7 +244,7 @@ impl CursorAgentProvider {
|
|||||||
println!("Original system prompt length: {} chars", system.len());
|
println!("Original system prompt length: {} chars", system.len());
|
||||||
println!(
|
println!(
|
||||||
"Filtered system prompt length: {} chars",
|
"Filtered system prompt length: {} chars",
|
||||||
self.filter_extensions_from_system_prompt(system).len()
|
filter_extensions_from_system_prompt(system).len()
|
||||||
);
|
);
|
||||||
println!("Full prompt: {}", prompt);
|
println!("Full prompt: {}", prompt);
|
||||||
println!("Model: {}", self.model.model_name);
|
println!("Model: {}", self.model.model_name);
|
||||||
|
|||||||
@@ -133,7 +133,9 @@ pub fn parse_streaming_response(sse_data: &str) -> Result<Message> {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let json_str = &line[6..]; // Remove "data: " prefix
|
let Some(json_str) = line.get(6..) else {
|
||||||
|
continue;
|
||||||
|
}; // Remove "data: " prefix
|
||||||
if json_str.trim().is_empty() || json_str.trim() == "[DONE]" {
|
if json_str.trim().is_empty() || json_str.trim() == "[DONE]" {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use tokio::process::Command;
|
|||||||
|
|
||||||
use super::base::{Provider, ProviderMetadata, ProviderUsage, Usage};
|
use super::base::{Provider, ProviderMetadata, ProviderUsage, Usage};
|
||||||
use super::errors::ProviderError;
|
use super::errors::ProviderError;
|
||||||
use super::utils::RequestLog;
|
use super::utils::{filter_extensions_from_system_prompt, RequestLog};
|
||||||
use crate::conversation::message::{Message, MessageContent};
|
use crate::conversation::message::{Message, MessageContent};
|
||||||
|
|
||||||
use crate::model::ModelConfig;
|
use crate::model::ModelConfig;
|
||||||
@@ -103,28 +103,6 @@ impl GeminiCliProvider {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Filter out the Extensions section from the system prompt
|
|
||||||
fn filter_extensions_from_system_prompt(&self, system: &str) -> String {
|
|
||||||
// Find the Extensions section and remove it
|
|
||||||
if let Some(extensions_start) = system.find("# Extensions") {
|
|
||||||
// Look for the next major section that starts with #
|
|
||||||
let after_extensions = &system[extensions_start..];
|
|
||||||
if let Some(next_section_pos) = after_extensions[1..].find("\n# ") {
|
|
||||||
// Found next section, keep everything before Extensions and after the next section
|
|
||||||
let before_extensions = &system[..extensions_start];
|
|
||||||
let next_section_start = extensions_start + next_section_pos + 1;
|
|
||||||
let after_next_section = &system[next_section_start..];
|
|
||||||
format!("{}{}", before_extensions.trim_end(), after_next_section)
|
|
||||||
} else {
|
|
||||||
// No next section found, just remove everything from Extensions onward
|
|
||||||
system[..extensions_start].trim_end().to_string()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// No Extensions section found, return original
|
|
||||||
system.to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Execute gemini CLI command with simple text prompt
|
/// Execute gemini CLI command with simple text prompt
|
||||||
async fn execute_command(
|
async fn execute_command(
|
||||||
&self,
|
&self,
|
||||||
@@ -135,8 +113,7 @@ impl GeminiCliProvider {
|
|||||||
// Create a simple prompt combining system + conversation
|
// Create a simple prompt combining system + conversation
|
||||||
let mut full_prompt = String::new();
|
let mut full_prompt = String::new();
|
||||||
|
|
||||||
// Add system prompt
|
let filtered_system = filter_extensions_from_system_prompt(system);
|
||||||
let filtered_system = self.filter_extensions_from_system_prompt(system);
|
|
||||||
full_prompt.push_str(&filtered_system);
|
full_prompt.push_str(&filtered_system);
|
||||||
full_prompt.push_str("\n\n");
|
full_prompt.push_str("\n\n");
|
||||||
|
|
||||||
|
|||||||
@@ -169,7 +169,9 @@ impl GithubCopilotProvider {
|
|||||||
if !tline.starts_with("data: ") {
|
if !tline.starts_with("data: ") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let payload = &tline[6..];
|
let Some(payload) = tline.get(6..) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
if payload == "[DONE]" {
|
if payload == "[DONE]" {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -246,7 +246,7 @@ impl SageMakerTgiProvider {
|
|||||||
// Remove any remaining HTML-like tags using a simple pattern
|
// Remove any remaining HTML-like tags using a simple pattern
|
||||||
// This is a basic implementation - for production use, consider using a proper HTML parser
|
// This is a basic implementation - for production use, consider using a proper HTML parser
|
||||||
while let Some(start) = result.find('<') {
|
while let Some(start) = result.find('<') {
|
||||||
if let Some(end) = result[start..].find('>') {
|
if let Some(end) = result.get(start..).and_then(|s| s.find('>')) {
|
||||||
result.replace_range(start..start + end + 1, "");
|
result.replace_range(start..start + end + 1, "");
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -47,6 +47,31 @@ pub fn convert_image(image: &ImageContent, image_format: &ImageFormat) -> Value
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn filter_extensions_from_system_prompt(system: &str) -> String {
|
||||||
|
let Some(extensions_start) = system.find("# Extensions") else {
|
||||||
|
return system.to_string();
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(after_extensions) = system.get(extensions_start + 1..) else {
|
||||||
|
return system.to_string();
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(next_section_pos) = after_extensions.find("\n# ") {
|
||||||
|
let Some(before) = system.get(..extensions_start) else {
|
||||||
|
return system.to_string();
|
||||||
|
};
|
||||||
|
let Some(after) = system.get(extensions_start + next_section_pos + 1..) else {
|
||||||
|
return system.to_string();
|
||||||
|
};
|
||||||
|
format!("{}{}", before.trim_end(), after)
|
||||||
|
} else {
|
||||||
|
system
|
||||||
|
.get(..extensions_start)
|
||||||
|
.map(|s| s.trim_end().to_string())
|
||||||
|
.unwrap_or_else(|| system.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn check_context_length_exceeded(text: &str) -> bool {
|
fn check_context_length_exceeded(text: &str) -> bool {
|
||||||
let check_phrases = [
|
let check_phrases = [
|
||||||
"too long",
|
"too long",
|
||||||
|
|||||||
@@ -275,7 +275,9 @@ data: [DONE]
|
|||||||
if !line.starts_with("data: ") {
|
if !line.starts_with("data: ") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let payload = &line[6..];
|
let Some(payload) = line.get(6..) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
if payload == "[DONE]" {
|
if payload == "[DONE]" {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -325,7 +327,9 @@ data: [DONE]
|
|||||||
if !line.starts_with("data: ") {
|
if !line.starts_with("data: ") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let payload = &line[6..];
|
let Some(payload) = line.get(6..) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
if payload == "[DONE]" {
|
if payload == "[DONE]" {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -377,7 +381,9 @@ data: [DONE]
|
|||||||
if !line.starts_with("data: ") {
|
if !line.starts_with("data: ") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let payload = &line[6..];
|
let Some(payload) = line.get(6..) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
if payload == "[DONE]" {
|
if payload == "[DONE]" {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ for provider_config in "${PROVIDERS[@]}"; do
|
|||||||
echo "Model: ${MODEL}"
|
echo "Model: ${MODEL}"
|
||||||
echo ""
|
echo ""
|
||||||
TMPFILE=$(mktemp)
|
TMPFILE=$(mktemp)
|
||||||
(cd "$TESTDIR" && "$SCRIPT_DIR/target/release/goose" run --text "please list files in the current directory" --with-builtin developer,autovisualiser,computercontroller,tutorial 2>&1) | tee "$TMPFILE"
|
(cd "$TESTDIR" && "$SCRIPT_DIR/target/release/goose" run --text "please list files in the current directory" --with-builtin developer,autovisualiser,computercontroller,tutorial,todo,extensionmanager 2>&1) | tee "$TMPFILE"
|
||||||
echo ""
|
echo ""
|
||||||
if grep -q "shell | developer" "$TMPFILE"; then
|
if grep -q "shell | developer" "$TMPFILE"; then
|
||||||
echo "✓ SUCCESS: Test passed - developer tool called"
|
echo "✓ SUCCESS: Test passed - developer tool called"
|
||||||
|
|||||||
Reference in New Issue
Block a user