syncable_cli/agent/tools/
analyze.rs1use 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#[derive(Debug, Deserialize)]
14pub struct AnalyzeArgs {
15 pub path: Option<String>,
17 pub mode: Option<String>,
19}
20
21#[derive(Debug, thiserror::Error)]
23#[error("Analysis error: {0}")]
24pub struct AnalyzeError(String);
25
26#[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 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 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}