chasm_cli/providers/
cursor.rs1use super::{ChatProvider, ProviderType};
6use crate::models::ChatSession;
7use anyhow::Result;
8use std::path::PathBuf;
9
10pub struct CursorProvider {
15 storage_path: PathBuf,
17 available: bool,
19}
20
21impl CursorProvider {
22 pub fn discover() -> Option<Self> {
24 let storage_path = Self::find_cursor_storage()?;
25
26 Some(Self {
27 available: storage_path.exists(),
28 storage_path,
29 })
30 }
31
32 fn find_cursor_storage() -> Option<PathBuf> {
34 #[cfg(target_os = "windows")]
35 {
36 let appdata = dirs::data_dir()?;
37 let cursor_path = appdata.join("Cursor").join("User").join("workspaceStorage");
38 if cursor_path.exists() {
39 return Some(cursor_path);
40 }
41 let roaming = std::env::var("APPDATA").ok()?;
43 let roaming_path = PathBuf::from(roaming)
44 .join("Cursor")
45 .join("User")
46 .join("workspaceStorage");
47 if roaming_path.exists() {
48 return Some(roaming_path);
49 }
50 }
51
52 #[cfg(target_os = "macos")]
53 {
54 let home = dirs::home_dir()?;
55 let cursor_path = home
56 .join("Library")
57 .join("Application Support")
58 .join("Cursor")
59 .join("User")
60 .join("workspaceStorage");
61 if cursor_path.exists() {
62 return Some(cursor_path);
63 }
64 }
65
66 #[cfg(target_os = "linux")]
67 {
68 let config = dirs::config_dir()?;
69 let cursor_path = config.join("Cursor").join("User").join("workspaceStorage");
70 if cursor_path.exists() {
71 return Some(cursor_path);
72 }
73 }
74
75 None
76 }
77
78 fn list_workspaces(&self) -> Result<Vec<PathBuf>> {
80 let mut workspaces = Vec::new();
81
82 if self.storage_path.exists() {
83 for entry in std::fs::read_dir(&self.storage_path)? {
84 let entry = entry?;
85 let path = entry.path();
86
87 if path.is_dir() {
88 let chat_path = path.join("chatSessions");
90 if chat_path.exists() {
91 workspaces.push(path);
92 }
93 }
94 }
95 }
96
97 Ok(workspaces)
98 }
99}
100
101impl ChatProvider for CursorProvider {
102 fn provider_type(&self) -> ProviderType {
103 ProviderType::Cursor
104 }
105
106 fn name(&self) -> &str {
107 "Cursor"
108 }
109
110 fn is_available(&self) -> bool {
111 self.available
112 }
113
114 fn sessions_path(&self) -> Option<PathBuf> {
115 Some(self.storage_path.clone())
116 }
117
118 fn list_sessions(&self) -> Result<Vec<ChatSession>> {
119 let mut sessions = Vec::new();
120
121 for workspace in self.list_workspaces()? {
122 let chat_path = workspace.join("chatSessions");
123
124 if chat_path.exists() {
125 for entry in std::fs::read_dir(&chat_path)? {
126 let entry = entry?;
127 let path = entry.path();
128
129 if path.extension().is_some_and(|e| e == "json") {
130 if let Ok(content) = std::fs::read_to_string(&path) {
131 if let Ok(session) = serde_json::from_str::<ChatSession>(&content) {
132 sessions.push(session);
133 }
134 }
135 }
136 }
137 }
138 }
139
140 Ok(sessions)
141 }
142
143 fn import_session(&self, session_id: &str) -> Result<ChatSession> {
144 for workspace in self.list_workspaces()? {
146 let session_path = workspace
147 .join("chatSessions")
148 .join(format!("{}.json", session_id));
149
150 if session_path.exists() {
151 let content = std::fs::read_to_string(&session_path)?;
152 let session: ChatSession = serde_json::from_str(&content)?;
153 return Ok(session);
154 }
155 }
156
157 anyhow::bail!("Session not found: {}", session_id)
158 }
159
160 fn export_session(&self, _session: &ChatSession) -> Result<()> {
161 anyhow::bail!("Export to Cursor not yet implemented - use import instead")
164 }
165}