Skip to content
Closed
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
67 changes: 67 additions & 0 deletions crates/goose-mcp/src/developer/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod editor_models;
mod lang;
mod shell;
mod task_tracker;

use anyhow::Result;
use base64::Engine;
Expand Down Expand Up @@ -39,6 +40,7 @@ use rmcp::object;

use self::editor_models::{create_editor_model, EditorModel};
use self::shell::{expand_path, get_shell_config, is_absolute_path, normalize_line_endings};
use self::task_tracker::TaskTracker;
use indoc::indoc;
use std::process::Stdio;
use std::sync::{Arc, Mutex};
Expand Down Expand Up @@ -100,6 +102,7 @@ pub struct DeveloperRouter {
file_history: Arc<Mutex<HashMap<PathBuf, Vec<String>>>>,
ignore_patterns: Arc<Gitignore>,
editor_model: Option<EditorModel>,
task_tracker: TaskTracker,
}

impl Default for DeveloperRouter {
Expand Down Expand Up @@ -345,6 +348,59 @@ impl DeveloperRouter {
open_world_hint: Some(false),
});

let task_tracker_tool = Tool::new(
"task_tracker",
indoc! {r#"
The task tracker tool will help you keep track of tasks
ALWAYS to use this when starting a non trivial activity to help you plan, and when resuming or shifting activities
ALWAYS check the task tracker tasks, and update it as you go
This is an ESSENTIAL tool for breaking down your work into chunks and ensuring it is completed
Check the list often, and update it (one by one) as you complete tasks
When starting out, you SHOULD plan your tasks in advance in very short description for each

use wip action when you start on one task at a time and done action when finished with it

for example,
user: "build me a time machine".
task list: "establish a view of quantum physics", "solve causality paradoxes", "research negative energy", "test time machine"

Each task has a status: to do, wip (work in progress), or done.
Note: Task descriptions must be 200 characters or less. Keep them concise
From time to time you may need to clear-tasks to start fresh

Actions:
- list: Show all tasks and their status
- add: Add a new task
- wip: Mark a task as work in progress
- done: Mark a task as completed
- clear-tasks: Remove all tasks and start fresh

Each task has a status: to do, wip (work in progress), or done.
"#},
object!({
"type": "object",
"required": ["action"],
"properties": {
"action": {
"type": "string",
"enum": ["list", "add", "wip", "done", "clear-tasks"],
"description": "The action to perform"
},
"task": {
"type": "string",
"description": "The brief task description (required for add, wip, done actions)"
}
}
}),
)
.annotate(ToolAnnotations {
title: Some("Task Tracker".to_string()),
read_only_hint: Some(false),
destructive_hint: Some(false),
idempotent_hint: Some(false),
open_world_hint: Some(false),
});

// Get base instructions and working directory
let cwd = std::env::current_dir().expect("should have a current working dir");
let os = std::env::consts::OS;
Expand Down Expand Up @@ -505,12 +561,14 @@ impl DeveloperRouter {
list_windows_tool,
screen_capture_tool,
image_processor_tool,
task_tracker_tool,
],
prompts: Arc::new(load_prompt_files()),
instructions,
file_history: Arc::new(Mutex::new(HashMap::new())),
ignore_patterns: Arc::new(ignore_patterns),
editor_model,
task_tracker: TaskTracker::new(),
}
}

Expand Down Expand Up @@ -1486,6 +1544,10 @@ impl DeveloperRouter {
Content::image(data, "image/png").with_priority(0.0),
])
}

async fn task_tracker(&self, params: Value) -> Result<Vec<Content>, ToolError> {
self.task_tracker.handle_request(params).await
}
}

fn recommend_read_range(path: &Path, total_lines: usize) -> Result<Vec<Content>, ToolError> {
Expand Down Expand Up @@ -1532,6 +1594,7 @@ impl Router for DeveloperRouter {
"list_windows" => this.list_windows(arguments).await,
"screen_capture" => this.screen_capture(arguments).await,
"image_processor" => this.image_processor(arguments).await,
"task_tracker" => this.task_tracker(arguments).await,
_ => Err(ToolError::NotFound(format!("Tool {} not found", tool_name))),
}
})
Expand Down Expand Up @@ -1589,6 +1652,7 @@ impl Clone for DeveloperRouter {
instructions: self.instructions.clone(),
file_history: Arc::clone(&self.file_history),
ignore_patterns: Arc::clone(&self.ignore_patterns),
task_tracker: self.task_tracker.clone(),
editor_model: create_editor_model(),
}
}
Expand Down Expand Up @@ -2056,6 +2120,7 @@ mod tests {
file_history: Arc::new(Mutex::new(HashMap::new())),
ignore_patterns: Arc::new(ignore_patterns),
editor_model: None,
task_tracker: TaskTracker::new(),
};

// Test basic file matching
Expand Down Expand Up @@ -2107,6 +2172,7 @@ mod tests {
file_history: Arc::new(Mutex::new(HashMap::new())),
ignore_patterns: Arc::new(ignore_patterns),
editor_model: None,
task_tracker: TaskTracker::new(),
};

// Try to write to an ignored file
Expand Down Expand Up @@ -2167,6 +2233,7 @@ mod tests {
file_history: Arc::new(Mutex::new(HashMap::new())),
ignore_patterns: Arc::new(ignore_patterns),
editor_model: None,
task_tracker: TaskTracker::new(),
};

// Create an ignored file
Expand Down
Loading
Loading