syncable_cli/agent/tools/
analyze.rs

1//! Analyze tool - wraps the analyze command using Rig's Tool trait
2
3use rig::completion::ToolDefinition;
4use rig::tool::Tool;
5use serde::{Deserialize, Serialize};
6use serde_json::json;
7use std::path::PathBuf;
8
9use crate::analyzer::display::{DisplayMode, display_analysis_to_string};
10use crate::analyzer::analyze_monorepo;
11
12/// Arguments for the analyze tool
13#[derive(Debug, Deserialize)]
14pub struct AnalyzeArgs {
15    /// Optional subdirectory path to analyze
16    pub path: Option<String>,
17    /// Display mode: "matrix" (default), "detailed", "summary", or "json"
18    pub mode: Option<String>,
19}
20
21/// Error type for analyze tool
22#[derive(Debug, thiserror::Error)]
23#[error("Analysis error: {0}")]
24pub struct AnalyzeError(String);
25
26/// Tool to analyze a project
27#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct AnalyzeTool {
29    project_path: PathBuf,
30}
31
32impl AnalyzeTool {
33    pub fn new(project_path: PathBuf) -> Self {
34        Self { project_path }
35    }
36}
37
38impl Tool for AnalyzeTool {
39    const NAME: &'static str = "analyze_project";
40
41    type Error = AnalyzeError;
42    type Args = AnalyzeArgs;
43    type Output = String;
44
45    async fn definition(&self, _prompt: String) -> ToolDefinition {
46        ToolDefinition {
47            name: Self::NAME.to_string(),
48            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. Use 'detailed' mode for full analysis, 'summary' for quick overview, 'json' for structured data.".to_string(),
49            parameters: json!({
50                "type": "object",
51                "properties": {
52                    "path": {
53                        "type": "string",
54                        "description": "Optional subdirectory path to analyze (relative to project root). If not provided, analyzes the entire project."
55                    },
56                    "mode": {
57                        "type": "string",
58                        "enum": ["matrix", "detailed", "summary", "json"],
59                        "description": "Display mode: 'matrix' for compact dashboard, 'detailed' for full analysis with Docker info, 'summary' for brief overview, 'json' for structured data. Default is 'json' for best agent parsing."
60                    }
61                }
62            }),
63        }
64    }
65
66    async fn call(&self, args: Self::Args) -> Result<Self::Output, Self::Error> {
67        let path = if let Some(subpath) = args.path {
68            self.project_path.join(subpath)
69        } else {
70            self.project_path.clone()
71        };
72
73        // Parse display mode - default to JSON for agent consumption
74        let display_mode = match args.mode.as_deref() {
75            Some("matrix") => DisplayMode::Matrix,
76            Some("detailed") => DisplayMode::Detailed,
77            Some("summary") => DisplayMode::Summary,
78            Some("json") | None => DisplayMode::Json,
79            _ => DisplayMode::Json,
80        };
81
82        match analyze_monorepo(&path) {
83            Ok(analysis) => {
84                // Use the display system to format output
85                let output = display_analysis_to_string(&analysis, display_mode);
86                Ok(output)
87            }
88            Err(e) => Err(AnalyzeError(format!("Analysis failed: {}", e))),
89        }
90    }
91}