Spaces:
Runtime error
Runtime error
File size: 5,028 Bytes
c62d538 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | //! Spec-Kit Implement Tool
//!
//! Executes implementation according to the task list.
use anyhow::{Context, Result};
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use std::path::PathBuf;
use crate::mcp::types::{ContentBlock, ToolDefinition, ToolResult};
use crate::speckit::SpecKitCli;
use crate::tools::Tool;
/// Parameters for the speckit_implement tool
#[derive(Debug, Deserialize, Serialize)]
pub struct ImplementParams {
/// Path to tasks file
task_file: PathBuf,
/// Additional context for implementation
#[serde(default)]
context: Option<String>,
/// Output directory for implementation
#[serde(default = "default_output_dir")]
output_dir: PathBuf,
}
fn default_output_dir() -> PathBuf {
PathBuf::from("./src")
}
/// Tool for executing implementation
pub struct ImplementTool {
#[allow(dead_code)]
cli: SpecKitCli,
}
impl ImplementTool {
/// Create a new implement tool
pub fn new(cli: SpecKitCli) -> Self {
Self { cli }
}
}
#[async_trait]
impl Tool for ImplementTool {
fn definition(&self) -> ToolDefinition {
ToolDefinition {
name: "speckit_implement".to_string(),
description: "Execute implementation according to the task list, generating code and documentation".to_string(),
input_schema: json!({
"type": "object",
"properties": {
"task_file": {
"type": "string",
"description": "Path to the tasks file (speckit.tasks)"
},
"context": {
"type": "string",
"description": "Additional context for implementation (e.g., existing code patterns, constraints)"
},
"output_dir": {
"type": "string",
"description": "Directory where code will be generated",
"default": "./src"
}
},
"required": ["task_file"]
})
}
}
async fn execute(&self, params: Value) -> Result<ToolResult> {
let params: ImplementParams =
serde_json::from_value(params).context("Failed to parse implement parameters")?;
tracing::info!(
task_file = %params.task_file.display(),
output_dir = %params.output_dir.display(),
"Executing implementation"
);
// Read the tasks file
let tasks_content = tokio::fs::read_to_string(¶ms.task_file)
.await
.context("Failed to read tasks file")?;
// For now, we return guidance since actual implementation
// requires AI-generated code based on specs
let message = format!(
"Implementation guidance based on {}\n\n\
The task file contains the following items:\n\
{}\n\n\
Implementation approach:\n\
1. Review each task in the task list\n\
2. Implement tasks in order, respecting dependencies\n\
3. Write tests alongside implementation\n\
4. Update documentation as you go\n\
5. Commit after completing each logical unit\n\n\
Context: {}\n\n\
Output directory: {}\n\n\
Next step: Begin implementing the first task",
params.task_file.display(),
tasks_content
.lines()
.take(10)
.collect::<Vec<_>>()
.join("\n"),
params.context.as_deref().unwrap_or("None provided"),
params.output_dir.display()
);
Ok(ToolResult {
content: vec![ContentBlock::text(message)],
is_error: None,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::tempdir;
use tokio::fs;
#[tokio::test]
async fn test_implement_tool_definition() {
let cli = SpecKitCli::new();
let tool = ImplementTool::new(cli);
let def = tool.definition();
assert_eq!(def.name, "speckit_implement");
assert!(!def.description.is_empty());
}
#[tokio::test]
async fn test_implement_tool_execute() {
let cli = SpecKitCli::new_test_mode();
let tool = ImplementTool::new(cli);
let dir = tempdir().unwrap();
let task_file = dir.path().join("tasks.md");
// Create dummy task file
fs::write(&task_file, "Task 1: Implement feature\nTask 2: Write tests")
.await
.unwrap();
let params = json!({
"task_file": task_file.to_str().unwrap(),
"context": "Using Rust 2021 edition",
"output_dir": dir.path().join("src").to_str().unwrap()
});
let result = tool.execute(params).await.unwrap();
assert!(result.is_error.is_none() || !result.is_error.unwrap());
}
}
|