cursorflow 1.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
cursorflow/__init__.py ADDED
@@ -0,0 +1,78 @@
1
+ """
2
+ CursorFlow - AI-guided universal testing framework
3
+
4
+ Simple data collection engine that enables Cursor to autonomously test UI
5
+ and iterate on designs with immediate visual feedback.
6
+
7
+ Declarative Actions | Batch Execution | Universal Log Collection | Visual Development
8
+ """
9
+
10
+ from pathlib import Path
11
+
12
+ # Main API - clean and simple
13
+ from .core.cursorflow import CursorFlow
14
+
15
+ # Core components (for advanced usage)
16
+ from .core.browser_engine import BrowserEngine
17
+ from .core.log_monitor import LogMonitor
18
+ from .core.error_correlator import ErrorCorrelator
19
+
20
+ def _get_version():
21
+ """Get version from git tag or fallback to default"""
22
+ try:
23
+ import subprocess
24
+ result = subprocess.run(
25
+ ['git', 'describe', '--tags', '--exact-match'],
26
+ capture_output=True,
27
+ text=True,
28
+ cwd=Path(__file__).parent.parent
29
+ )
30
+ if result.returncode == 0:
31
+ # Remove 'v' prefix if present
32
+ return result.stdout.strip().lstrip('v')
33
+ except Exception:
34
+ pass
35
+
36
+ try:
37
+ # Try to get latest tag if not on exact tag
38
+ result = subprocess.run(
39
+ ['git', 'describe', '--tags', '--abbrev=0'],
40
+ capture_output=True,
41
+ text=True,
42
+ cwd=Path(__file__).parent.parent
43
+ )
44
+ if result.returncode == 0:
45
+ tag = result.stdout.strip().lstrip('v')
46
+ # Add dev suffix if not on exact tag
47
+ return f"{tag}-dev"
48
+ except Exception:
49
+ pass
50
+
51
+ # Fallback version
52
+ return "1.0.0-dev"
53
+
54
+ __version__ = _get_version()
55
+ __author__ = "GeekWarrior Development"
56
+
57
+ # Simple public API
58
+ __all__ = [
59
+ "CursorFlow", # Main interface for Cursor
60
+ "BrowserEngine", # Advanced browser control
61
+ "LogMonitor", # Advanced log monitoring
62
+ "ErrorCorrelator", # Advanced correlation analysis
63
+ "check_for_updates", # Update checking
64
+ "update_cursorflow", # Update management
65
+ ]
66
+
67
+ # Update functions (for programmatic access)
68
+ def check_for_updates(project_dir: str = "."):
69
+ """Check for CursorFlow updates"""
70
+ import asyncio
71
+ from .updater import check_updates
72
+ return asyncio.run(check_updates(project_dir))
73
+
74
+ def update_cursorflow(project_dir: str = ".", force: bool = False):
75
+ """Update CursorFlow package and rules"""
76
+ import asyncio
77
+ from .updater import update_cursorflow as _update
78
+ return asyncio.run(_update(project_dir, force=force))
@@ -0,0 +1,244 @@
1
+ """
2
+ CursorFlow Auto-Update Integration
3
+
4
+ Automatic update checking and notification system for seamless
5
+ CursorFlow maintenance across projects.
6
+ """
7
+
8
+ import asyncio
9
+ import json
10
+ import time
11
+ from pathlib import Path
12
+ from typing import Dict, Optional
13
+ import logging
14
+ from .updater import CursorFlowUpdater
15
+
16
+
17
+ class AutoUpdateManager:
18
+ """
19
+ Manages automatic update checking and notifications
20
+
21
+ Integrates with CursorFlow initialization to check for updates
22
+ periodically and notify users of available updates.
23
+ """
24
+
25
+ def __init__(self, project_dir: str = "."):
26
+ self.project_dir = Path(project_dir).resolve()
27
+ self.logger = logging.getLogger(__name__)
28
+ self.updater = CursorFlowUpdater(str(self.project_dir))
29
+
30
+ # Load preferences
31
+ self.preferences = self._load_preferences()
32
+
33
+ async def check_if_update_needed(self) -> Optional[Dict]:
34
+ """
35
+ Check if an update check is needed based on interval
36
+
37
+ Returns:
38
+ Update info if check was performed, None if not needed
39
+ """
40
+ try:
41
+ # Check if enough time has passed
42
+ if not self._should_check_for_updates():
43
+ return None
44
+
45
+ # Perform update check
46
+ update_info = await self.updater.check_for_updates(silent=True)
47
+
48
+ # Update last check time
49
+ self._update_last_check_time()
50
+
51
+ return update_info
52
+
53
+ except Exception as e:
54
+ self.logger.error(f"Auto-update check failed: {e}")
55
+ return None
56
+
57
+ async def notify_if_updates_available(self) -> bool:
58
+ """
59
+ Check for updates and notify if available
60
+
61
+ Returns:
62
+ True if updates are available, False otherwise
63
+ """
64
+ update_info = await self.check_if_update_needed()
65
+
66
+ if not update_info:
67
+ return False
68
+
69
+ has_updates = (
70
+ update_info.get("version_update_available", False) or
71
+ update_info.get("rules_update_available", False) or
72
+ not update_info.get("dependencies_current", True)
73
+ )
74
+
75
+ if has_updates:
76
+ self._display_update_notification(update_info)
77
+ return True
78
+
79
+ return False
80
+
81
+ def _should_check_for_updates(self) -> bool:
82
+ """Check if enough time has passed for update check"""
83
+ interval_hours = self.preferences.get("check_interval_hours", 24)
84
+ last_check = self.preferences.get("last_check")
85
+
86
+ if not last_check:
87
+ return True
88
+
89
+ try:
90
+ last_check_time = time.fromisoformat(last_check)
91
+ current_time = time.time()
92
+ hours_elapsed = (current_time - last_check_time) / 3600
93
+
94
+ return hours_elapsed >= interval_hours
95
+ except Exception:
96
+ return True
97
+
98
+ def _load_preferences(self) -> Dict:
99
+ """Load update preferences"""
100
+ prefs_file = self.project_dir / ".cursorflow" / "update_preferences.json"
101
+
102
+ if prefs_file.exists():
103
+ try:
104
+ with open(prefs_file) as f:
105
+ return json.load(f)
106
+ except Exception:
107
+ pass
108
+
109
+ # Default preferences
110
+ return {
111
+ "check_interval_hours": 24,
112
+ "auto_update": False,
113
+ "include_prereleases": False,
114
+ "backup_before_update": True,
115
+ "last_check": None
116
+ }
117
+
118
+ def _update_last_check_time(self):
119
+ """Update the last check timestamp"""
120
+ prefs_file = self.project_dir / ".cursorflow" / "update_preferences.json"
121
+ prefs_file.parent.mkdir(exist_ok=True)
122
+
123
+ self.preferences["last_check"] = time.time()
124
+
125
+ with open(prefs_file, 'w') as f:
126
+ json.dump(self.preferences, f, indent=2)
127
+
128
+ def _display_update_notification(self, update_info: Dict):
129
+ """Display update notification"""
130
+ print("\n" + "="*60)
131
+ print("šŸ”„ CursorFlow Updates Available!")
132
+ print("="*60)
133
+
134
+ if update_info.get("version_update_available"):
135
+ current = update_info.get("current_version", "unknown")
136
+ latest = update_info.get("latest_version", "unknown")
137
+ print(f"šŸ“¦ Package update: {current} → {latest}")
138
+
139
+ if update_info.get("rules_update_available"):
140
+ print("šŸ“ Rules update available")
141
+
142
+ if not update_info.get("dependencies_current"):
143
+ print("šŸ”§ Dependency updates available")
144
+
145
+ print("\nšŸ’” Update commands:")
146
+ print(" cursorflow update # Update everything")
147
+ print(" cursorflow check-updates # Check status")
148
+ print(" cursorflow install-deps # Update dependencies only")
149
+
150
+ print("\nāš™ļø To disable these notifications:")
151
+ print(" Edit .cursorflow/update_preferences.json")
152
+ print(" Set 'check_interval_hours' to 0")
153
+ print("="*60)
154
+
155
+
156
+ async def check_for_updates_on_startup(project_dir: str = ".") -> bool:
157
+ """
158
+ Check for updates during CursorFlow startup
159
+
160
+ Args:
161
+ project_dir: Project directory path
162
+
163
+ Returns:
164
+ True if updates are available, False otherwise
165
+ """
166
+ try:
167
+ manager = AutoUpdateManager(project_dir)
168
+ return await manager.notify_if_updates_available()
169
+ except Exception:
170
+ return False
171
+
172
+
173
+ def integrate_with_cursorflow():
174
+ """
175
+ Integration point for CursorFlow main class
176
+
177
+ Add this to CursorFlow.__init__ to enable auto-update checking
178
+ """
179
+
180
+ # Check for updates asynchronously in background
181
+ async def background_update_check():
182
+ try:
183
+ await check_for_updates_on_startup()
184
+ except Exception:
185
+ pass # Silent failure for background task
186
+
187
+ # Schedule background check
188
+ try:
189
+ loop = asyncio.get_event_loop()
190
+ if loop.is_running():
191
+ # If loop is already running, schedule as task
192
+ loop.create_task(background_update_check())
193
+ else:
194
+ # If no loop running, run briefly
195
+ asyncio.run(background_update_check())
196
+ except Exception:
197
+ pass # Silent failure
198
+
199
+
200
+ class UpdateScheduler:
201
+ """
202
+ Schedules periodic update checks during CursorFlow usage
203
+ """
204
+
205
+ def __init__(self, project_dir: str = "."):
206
+ self.project_dir = project_dir
207
+ self.manager = AutoUpdateManager(project_dir)
208
+ self._check_task = None
209
+
210
+ def start_periodic_checking(self, interval_minutes: int = 60):
211
+ """Start periodic update checking"""
212
+ if self._check_task and not self._check_task.done():
213
+ return # Already running
214
+
215
+ async def periodic_check():
216
+ while True:
217
+ try:
218
+ await self.manager.check_if_update_needed()
219
+ await asyncio.sleep(interval_minutes * 60)
220
+ except asyncio.CancelledError:
221
+ break
222
+ except Exception:
223
+ await asyncio.sleep(interval_minutes * 60)
224
+
225
+ self._check_task = asyncio.create_task(periodic_check())
226
+
227
+ def stop_periodic_checking(self):
228
+ """Stop periodic update checking"""
229
+ if self._check_task and not self._check_task.done():
230
+ self._check_task.cancel()
231
+
232
+
233
+ # Singleton scheduler for global use
234
+ _global_scheduler = None
235
+
236
+
237
+ def get_update_scheduler(project_dir: str = ".") -> UpdateScheduler:
238
+ """Get or create global update scheduler"""
239
+ global _global_scheduler
240
+
241
+ if _global_scheduler is None:
242
+ _global_scheduler = UpdateScheduler(project_dir)
243
+
244
+ return _global_scheduler