syncable_cli/agent/tools/
analyze.rs1use super::compression::{CompressionConfig, compress_analysis_output};
4use rig::completion::ToolDefinition;
5use rig::tool::Tool;
6use serde::{Deserialize, Serialize};
7use serde_json::json;
8use std::path::PathBuf;
9
10#[derive(Debug, Deserialize)]
12pub struct AnalyzeArgs {
13 pub path: Option<String>,
15}
16
17#[derive(Debug, thiserror::Error)]
19#[error("Analysis error: {0}")]
20pub struct AnalyzeError(String);
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct AnalyzeTool {
25 project_path: PathBuf,
26}
27
28impl AnalyzeTool {
29 pub fn new(project_path: PathBuf) -> Self {
30 Self { project_path }
31 }
32}
33
34impl Tool for AnalyzeTool {
35 const NAME: &'static str = "analyze_project";
36
37 type Error = AnalyzeError;
38 type Args = AnalyzeArgs;
39 type Output = String;
40
41 async fn definition(&self, _prompt: String) -> ToolDefinition {
42 ToolDefinition {
43 name: Self::NAME.to_string(),
44 description: "Analyze the project to detect programming languages, frameworks, dependencies, build tools, and architecture patterns. Returns a comprehensive overview of the project's technology stack.".to_string(),
45 parameters: json!({
46 "type": "object",
47 "properties": {
48 "path": {
49 "type": "string",
50 "description": "Optional subdirectory path to analyze (relative to project root). If not provided, analyzes the entire project."
51 }
52 }
53 }),
54 }
55 }
56
57 async fn call(&self, args: Self::Args) -> Result<Self::Output, Self::Error> {
58 let path = if let Some(subpath) = args.path {
59 self.project_path.join(subpath)
60 } else {
61 self.project_path.clone()
62 };
63
64 match crate::analyzer::analyze_monorepo(&path) {
67 Ok(analysis) => {
68 let json_value = serde_json::to_value(&analysis)
69 .map_err(|e| AnalyzeError(format!("Failed to serialize: {}", e)))?;
70
71 let config = CompressionConfig::default();
74 Ok(compress_analysis_output(&json_value, &config))
75 }
76 Err(e) => Err(AnalyzeError(format!("Analysis failed: {}", e))),
77 }
78 }
79}