cursorflow 2.1.5__py3-none-any.whl → 2.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.
@@ -0,0 +1,216 @@
1
+ """
2
+ Configuration Validation
3
+
4
+ Validates user-provided configuration against Playwright API.
5
+ Provides clear error messages with links to documentation.
6
+ """
7
+
8
+ from typing import Dict, Any, Set
9
+ import logging
10
+
11
+
12
+ class ConfigValidationError(Exception):
13
+ """Raised when configuration is invalid"""
14
+ pass
15
+
16
+
17
+ class ConfigValidator:
18
+ """
19
+ Validates CursorFlow and Playwright configuration
20
+
21
+ Strategy: We don't strictly validate - we warn about likely errors
22
+ and let Playwright do final validation. This keeps us forward-compatible.
23
+ """
24
+
25
+ # Common browser launch options (for helpful warnings)
26
+ # See: https://playwright.dev/python/docs/api/class-browsertype#browser-type-launch
27
+ KNOWN_BROWSER_OPTIONS = {
28
+ 'args', 'channel', 'chromium_sandbox', 'devtools', 'downloads_path',
29
+ 'env', 'executable_path', 'firefox_user_prefs', 'handle_sigint',
30
+ 'handle_sigterm', 'handle_sighup', 'headless', 'ignore_default_args',
31
+ 'proxy', 'slow_mo', 'timeout', 'traces_dir'
32
+ }
33
+
34
+ # Common context options (for helpful warnings)
35
+ # See: https://playwright.dev/python/docs/api/class-browser#browser-new-context
36
+ KNOWN_CONTEXT_OPTIONS = {
37
+ 'accept_downloads', 'base_url', 'bypass_csp', 'color_scheme',
38
+ 'device_scale_factor', 'extra_http_headers', 'forced_colors',
39
+ 'geolocation', 'has_touch', 'http_credentials', 'ignore_https_errors',
40
+ 'is_mobile', 'java_script_enabled', 'locale', 'no_viewport',
41
+ 'offline', 'permissions', 'proxy', 'record_har_content',
42
+ 'record_har_mode', 'record_har_omit_content', 'record_har_path',
43
+ 'record_har_url_filter', 'record_video_dir', 'record_video_size',
44
+ 'reduced_motion', 'screen', 'service_workers', 'storage_state',
45
+ 'strict_selectors', 'timezone_id', 'user_agent', 'viewport'
46
+ }
47
+
48
+ @classmethod
49
+ def validate_browser_options(cls, options: Dict[str, Any]) -> Dict[str, Any]:
50
+ """
51
+ Validate browser launch options
52
+
53
+ Args:
54
+ options: User-provided browser options
55
+
56
+ Returns:
57
+ Validated options (unchanged - just warnings logged)
58
+ """
59
+ logger = logging.getLogger(__name__)
60
+
61
+ # Warn about unknown options (might be typos)
62
+ for key in options.keys():
63
+ if key not in cls.KNOWN_BROWSER_OPTIONS:
64
+ logger.warning(
65
+ f"Unknown browser option '{key}' - will pass to Playwright anyway. "
66
+ f"Check spelling or see: https://playwright.dev/python/docs/api/class-browsertype#browser-type-launch"
67
+ )
68
+
69
+ # Validate specific option types
70
+ if 'headless' in options and not isinstance(options['headless'], bool):
71
+ raise ConfigValidationError(
72
+ f"'headless' must be boolean, got {type(options['headless']).__name__}: {options['headless']}"
73
+ )
74
+
75
+ if 'timeout' in options and not isinstance(options['timeout'], (int, float)):
76
+ raise ConfigValidationError(
77
+ f"'timeout' must be number, got {type(options['timeout']).__name__}: {options['timeout']}"
78
+ )
79
+
80
+ if 'args' in options and not isinstance(options['args'], list):
81
+ raise ConfigValidationError(
82
+ f"'args' must be list of strings, got {type(options['args']).__name__}"
83
+ )
84
+
85
+ return options
86
+
87
+ @classmethod
88
+ def validate_context_options(cls, options: Dict[str, Any]) -> Dict[str, Any]:
89
+ """
90
+ Validate browser context options
91
+
92
+ Args:
93
+ options: User-provided context options
94
+
95
+ Returns:
96
+ Validated options (unchanged - just warnings logged)
97
+ """
98
+ logger = logging.getLogger(__name__)
99
+
100
+ # Warn about unknown options
101
+ for key in options.keys():
102
+ if key not in cls.KNOWN_CONTEXT_OPTIONS:
103
+ logger.warning(
104
+ f"Unknown context option '{key}' - will pass to Playwright anyway. "
105
+ f"Check spelling or see: https://playwright.dev/python/docs/api/class-browser#browser-new-context"
106
+ )
107
+
108
+ # Validate specific option types
109
+ if 'viewport' in options:
110
+ viewport = options['viewport']
111
+ if not isinstance(viewport, dict):
112
+ raise ConfigValidationError(
113
+ f"'viewport' must be dict with width/height, got {type(viewport).__name__}"
114
+ )
115
+ if 'width' in viewport and not isinstance(viewport['width'], int):
116
+ raise ConfigValidationError(
117
+ f"viewport width must be integer, got {type(viewport['width']).__name__}"
118
+ )
119
+ if 'height' in viewport and not isinstance(viewport['height'], int):
120
+ raise ConfigValidationError(
121
+ f"viewport height must be integer, got {type(viewport['height']).__name__}"
122
+ )
123
+
124
+ if 'geolocation' in options:
125
+ geo = options['geolocation']
126
+ if not isinstance(geo, dict) or 'latitude' not in geo or 'longitude' not in geo:
127
+ raise ConfigValidationError(
128
+ f"'geolocation' must be dict with latitude/longitude: "
129
+ f"{{'latitude': 40.7128, 'longitude': -74.0060}}"
130
+ )
131
+
132
+ if 'timezone_id' in options and not isinstance(options['timezone_id'], str):
133
+ raise ConfigValidationError(
134
+ f"'timezone_id' must be string like 'America/New_York', got {type(options['timezone_id']).__name__}"
135
+ )
136
+
137
+ return options
138
+
139
+ @classmethod
140
+ def get_config_examples(cls) -> str:
141
+ """Get example configurations for documentation"""
142
+ return """
143
+ Browser Configuration Examples:
144
+
145
+ Enable DevTools (non-headless):
146
+ {
147
+ "headless": false,
148
+ "browser_launch_options": {
149
+ "devtools": true
150
+ }
151
+ }
152
+
153
+ Use specific Chrome channel:
154
+ {
155
+ "browser_launch_options": {
156
+ "channel": "chrome"
157
+ }
158
+ }
159
+
160
+ Custom proxy:
161
+ {
162
+ "browser_launch_options": {
163
+ "proxy": {
164
+ "server": "http://myproxy.com:3128",
165
+ "username": "user",
166
+ "password": "pass"
167
+ }
168
+ }
169
+ }
170
+
171
+ Context Configuration Examples:
172
+
173
+ Test in dark mode:
174
+ {
175
+ "context_options": {
176
+ "color_scheme": "dark"
177
+ }
178
+ }
179
+
180
+ Test with geolocation:
181
+ {
182
+ "context_options": {
183
+ "geolocation": {"latitude": 40.7128, "longitude": -74.0060},
184
+ "permissions": ["geolocation"]
185
+ }
186
+ }
187
+
188
+ Test offline behavior:
189
+ {
190
+ "context_options": {
191
+ "offline": true
192
+ }
193
+ }
194
+
195
+ Custom timezone:
196
+ {
197
+ "context_options": {
198
+ "timezone_id": "America/Los_Angeles"
199
+ }
200
+ }
201
+
202
+ HTTP authentication:
203
+ {
204
+ "context_options": {
205
+ "http_credentials": {
206
+ "username": "admin",
207
+ "password": "secret"
208
+ }
209
+ }
210
+ }
211
+
212
+ See Playwright documentation for all available options:
213
+ Browser: https://playwright.dev/python/docs/api/class-browsertype#browser-type-launch
214
+ Context: https://playwright.dev/python/docs/api/class-browser#browser-new-context
215
+ """
216
+
@@ -669,6 +669,15 @@ class CursorFlow:
669
669
 
670
670
  async def _execute_actions(self, actions: List[Dict]) -> bool:
671
671
  """Execute list of declarative actions"""
672
+ # Validate all actions before execution
673
+ from .action_validator import ActionValidator, ActionValidationError
674
+
675
+ try:
676
+ actions = ActionValidator.validate_list(actions)
677
+ except ActionValidationError as e:
678
+ self.logger.error(f"Action validation failed: {e}")
679
+ return False
680
+
672
681
  try:
673
682
  for action in actions:
674
683
  await self._execute_single_action(action)
@@ -678,44 +687,26 @@ class CursorFlow:
678
687
  return False
679
688
 
680
689
  async def _execute_single_action(self, action: Dict):
681
- """Execute a single declarative action and track it"""
690
+ """
691
+ Execute a single declarative action and track it
692
+
693
+ Pass-through architecture: CursorFlow handles a few special actions,
694
+ then passes everything else directly to Playwright Page API.
695
+ """
682
696
  action_start = time.time()
683
697
 
684
698
  try:
685
- # Navigation actions
686
- if "navigate" in action:
699
+ # Extract action type
700
+ action_type = action.get('type') or list(action.keys())[0]
701
+
702
+ # CursorFlow-specific actions (not direct Playwright methods)
703
+ if action_type == "navigate":
687
704
  url = action["navigate"]
688
705
  if isinstance(url, dict):
689
706
  url = url["url"]
690
707
  await self.browser.navigate(url)
691
708
 
692
- # Interaction actions
693
- elif "click" in action:
694
- selector = action["click"]
695
- if isinstance(selector, dict):
696
- selector = selector["selector"]
697
- await self.browser.click(selector)
698
-
699
- elif "fill" in action:
700
- config = action["fill"]
701
- await self.browser.fill(config["selector"], config["value"])
702
-
703
- elif "type" in action:
704
- config = action["type"]
705
- await self.browser.type(config["selector"], config["text"])
706
-
707
- # Waiting actions
708
- elif "wait" in action:
709
- await asyncio.sleep(action["wait"])
710
-
711
- elif "wait_for" in action:
712
- selector = action["wait_for"]
713
- if isinstance(selector, dict):
714
- selector = selector["selector"]
715
- await self.browser.wait_for_element(selector)
716
-
717
- # Capture actions
718
- elif "screenshot" in action:
709
+ elif action_type == "screenshot":
719
710
  screenshot_config = action["screenshot"]
720
711
 
721
712
  # Handle both string and dict formats
@@ -734,15 +725,19 @@ class CursorFlow:
734
725
  screenshot_data = await self.browser.screenshot(name, options)
735
726
  self.artifacts["screenshots"].append(screenshot_data)
736
727
 
737
- elif "authenticate" in action:
728
+ elif action_type == "authenticate":
738
729
  if self.auth_handler:
739
730
  await self.auth_handler.authenticate(self.browser.page, action["authenticate"])
731
+
732
+ # Pass-through to Playwright Page API (hover, dblclick, press, etc.)
733
+ else:
734
+ await self._execute_playwright_action(action_type, action)
740
735
 
741
736
  # Record action in timeline
742
737
  self.timeline.append({
743
738
  "timestamp": action_start,
744
739
  "type": "browser",
745
- "event": list(action.keys())[0],
740
+ "event": action_type,
746
741
  "data": action,
747
742
  "duration": time.time() - action_start
748
743
  })
@@ -751,6 +746,47 @@ class CursorFlow:
751
746
  self.logger.error(f"Action failed: {action}, error: {e}")
752
747
  raise
753
748
 
749
+ async def _execute_playwright_action(self, action_type: str, action: Dict):
750
+ """
751
+ Pass-through to Playwright Page API
752
+
753
+ Enables using any Playwright method without hardcoding them:
754
+ - hover, dblclick, drag_and_drop
755
+ - press, focus, blur, check, uncheck
756
+ - evaluate, route, expose_function
757
+ - And 90+ more methods
758
+
759
+ See: https://playwright.dev/python/docs/api/class-page
760
+ """
761
+ # Get the action config
762
+ action_config = action.get(action_type)
763
+
764
+ # Check if method exists on Playwright Page
765
+ if hasattr(self.browser.page, action_type):
766
+ method = getattr(self.browser.page, action_type)
767
+
768
+ # Call with appropriate args based on config format
769
+ if isinstance(action_config, str):
770
+ # Simple format: {"hover": ".selector"}
771
+ await method(action_config)
772
+ elif isinstance(action_config, dict):
773
+ # Config format: {"hover": {"selector": ".item", "timeout": 5000}}
774
+ await method(**action_config)
775
+ elif action_config is None:
776
+ # No args: {"reload": null}
777
+ await method()
778
+ else:
779
+ # Numeric or other: {"wait": 2}
780
+ await method(action_config)
781
+ else:
782
+ # Method doesn't exist on Page - provide helpful error
783
+ available_methods = [m for m in dir(self.browser.page) if not m.startswith('_') and callable(getattr(self.browser.page, m))]
784
+ raise AttributeError(
785
+ f"Unknown Playwright action: '{action_type}'\n"
786
+ f"Available methods: {', '.join(sorted(available_methods[:20]))}...\n"
787
+ f"Full API: https://playwright.dev/python/docs/api/class-page"
788
+ )
789
+
754
790
  async def _cleanup_session(self, session_options: Dict):
755
791
  """Clean up browser session"""
756
792
  try:
@@ -80,10 +80,9 @@ def install_cursorflow_rules(project_dir: str = ".", force: bool = False):
80
80
  setup_update_checking(project_path)
81
81
 
82
82
  print(f"\n📝 Next steps:")
83
- print(f" 1. Review cursorflow-config.json and update for your project")
84
- print(f" 2. Install CursorFlow: pip install cursorflow")
85
- print(f" 3. Install Playwright: playwright install chromium")
86
- print(f" 4. Start using CursorFlow for UI testing and CSS iteration!")
83
+ print(f" 1. Review .cursorflow/config.json and update for your project")
84
+ print(f" 2. Install Playwright: playwright install chromium")
85
+ print(f" 3. Start using CursorFlow for UI testing and CSS iteration!")
87
86
  print(f"\n🔄 Update commands:")
88
87
  print(f" - Check for updates: python -m cursorflow check-updates")
89
88
  print(f" - Update CursorFlow: python -m cursorflow update")
@@ -120,18 +119,22 @@ cursorflow_session_*.json
120
119
  def create_config_template(project_path: Path, force: bool = False):
121
120
  """Create or update CursorFlow configuration template"""
122
121
 
123
- config_path = project_path / "cursorflow-config.json"
122
+ # Create .cursorflow directory if it doesn't exist
123
+ cursorflow_dir = project_path / ".cursorflow"
124
+ cursorflow_dir.mkdir(exist_ok=True)
125
+
126
+ config_path = cursorflow_dir / "config.json"
124
127
 
125
128
  # Get current version
126
129
  try:
127
130
  import cursorflow
128
- current_version = getattr(cursorflow, '__version__', '2.1.5')
131
+ current_version = getattr(cursorflow, '__version__', '2.2.0')
129
132
  except ImportError:
130
- current_version = '2.1.5'
133
+ current_version = '2.2.0'
131
134
 
132
135
  if config_path.exists():
133
136
  if not force:
134
- print("ℹ️ cursorflow-config.json already exists (use --force to recreate)")
137
+ print("ℹ️ .cursorflow/config.json already exists (use --force to recreate)")
135
138
  # Smart update: only update version and add missing fields
136
139
  try:
137
140
  with open(config_path) as f:
@@ -198,7 +201,7 @@ def create_config_template(project_path: Path, force: bool = False):
198
201
  json.dump(config_template, f, indent=2)
199
202
 
200
203
  action = "Recreated" if force else "Created"
201
- print(f"✅ {action} configuration template: cursorflow-config.json")
204
+ print(f"✅ {action} configuration: .cursorflow/config.json")
202
205
  print(f" Detected project type: {project_type}")
203
206
  print(f" CursorFlow version: {current_version}")
204
207
 
@@ -302,9 +305,9 @@ def setup_update_checking(project_path: Path):
302
305
  # Create initial version tracking
303
306
  try:
304
307
  import cursorflow
305
- current_version = getattr(cursorflow, '__version__', '2.1.5')
308
+ current_version = getattr(cursorflow, '__version__', '2.2.0')
306
309
  except ImportError:
307
- current_version = '2.1.5'
310
+ current_version = '2.2.0'
308
311
 
309
312
  version_info = {
310
313
  "installed_version": current_version,
@@ -168,8 +168,27 @@ class LocalFileLogSource:
168
168
  """Connect to log sources - compatibility method for log_collector"""
169
169
  return await self.start_monitoring()
170
170
 
171
+ async def get_new_entries(self) -> List[Dict]:
172
+ """
173
+ Get new log entries since last call (required interface method)
174
+
175
+ Returns:
176
+ List of log entry dicts with timestamp, content, source
177
+ """
178
+ new_entries = []
179
+
180
+ # Drain queue of all available entries
181
+ while not self.log_queue.empty():
182
+ try:
183
+ log_entry = self.log_queue.get_nowait()
184
+ new_entries.append(log_entry)
185
+ except queue.Empty:
186
+ break
187
+
188
+ return new_entries
189
+
171
190
  def get_recent_logs(self, seconds: int = 10) -> List[Dict]:
172
- """Get logs from the last N seconds"""
191
+ """Get logs from the last N seconds (non-destructive)"""
173
192
  from datetime import timedelta
174
193
 
175
194
  cutoff_time = datetime.now() - timedelta(seconds=seconds)
@@ -165,6 +165,25 @@ class SSHRemoteLogSource:
165
165
  """Connect to SSH log sources - compatibility method for log_collector"""
166
166
  return await self.start_monitoring()
167
167
 
168
+ async def get_new_entries(self) -> List[Dict]:
169
+ """
170
+ Get new log entries since last call (required interface method)
171
+
172
+ Returns:
173
+ List of log entry dicts with timestamp, content, source
174
+ """
175
+ new_entries = []
176
+
177
+ # Drain queue of all available entries
178
+ while not self.log_queue.empty():
179
+ try:
180
+ log_entry = self.log_queue.get_nowait()
181
+ new_entries.append(log_entry)
182
+ except queue.Empty:
183
+ break
184
+
185
+ return new_entries
186
+
168
187
  def get_recent_logs(self, seconds: int = 10) -> List[Dict]:
169
188
  """Get logs from the last N seconds without stopping monitoring"""
170
189
 
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  title: CursorFlow Installation & Setup Guide for Cursor
3
3
  description: How to install and configure CursorFlow for complete page intelligence in AI-driven development
4
+ alwaysApply: true
4
5
  ---
5
6
 
6
7
  # CursorFlow Installation & Setup Guide for Cursor
@@ -24,7 +25,7 @@ CursorFlow requires **TWO installations**:
24
25
  This step created THIS FILE you're reading right now.
25
26
 
26
27
  **If Cursor reports "command not found: cursorflow"**, the user needs Step 1.
27
- **If you can run cursorflow but tests fail**, check `cursorflow-config.json` in project root.
28
+ **If you can run cursorflow but tests fail**, check `.cursorflow/config.json` configuration.
28
29
 
29
30
  ## 🚀 **When to Install CursorFlow**
30
31
 
@@ -1,6 +1,12 @@
1
+ ---
2
+ title: CursorFlow CLI Usage Guide for Cursor AI
3
+ description: Complete guide for using CursorFlow CLI with comprehensive page intelligence and data collection
4
+ alwaysApply: true
5
+ ---
6
+
1
7
  # CursorFlow Usage Rules for Cursor AI
2
8
 
3
- ## 🔥 **CursorFlow 2.1.4: Complete Page Intelligence for AI-Driven Development**
9
+ ## 🔥 **CursorFlow: Complete Page Intelligence for AI-Driven Development**
4
10
 
5
11
  **CursorFlow provides comprehensive data collection for rapid UI iteration:**
6
12
 
cursorflow/updater.py CHANGED
@@ -363,7 +363,7 @@ class CursorFlowUpdater:
363
363
 
364
364
  async def _migrate_configuration(self) -> bool:
365
365
  """Migrate configuration to new format if needed"""
366
- config_file = self.project_dir / "cursorflow-config.json"
366
+ config_file = self.project_dir / ".cursorflow" / "config.json"
367
367
 
368
368
  if not config_file.exists():
369
369
  return True
@@ -417,9 +417,10 @@ class CursorFlowUpdater:
417
417
  backup_path = backup_dir / backup_name
418
418
 
419
419
  # Backup configuration and rules
420
- if (self.project_dir / "cursorflow-config.json").exists():
420
+ config_file = self.project_dir / ".cursorflow" / "config.json"
421
+ if config_file.exists():
421
422
  shutil.copy2(
422
- self.project_dir / "cursorflow-config.json",
423
+ config_file,
423
424
  backup_path.with_suffix('.config.json')
424
425
  )
425
426
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cursorflow
3
- Version: 2.1.5
3
+ Version: 2.2.0
4
4
  Summary: 🔥 Complete page intelligence for AI-driven development with Hot Reload Intelligence - captures DOM, network, console, performance, HMR events, and comprehensive page analysis
5
5
  Author-email: GeekWarrior Development <rbush@cooltheory.com>
6
6
  License-Expression: MIT
@@ -82,20 +82,57 @@ When CursorFlow reports `"average_response_time": 416.58ms`, you can tell stakeh
82
82
 
83
83
  **Documentary vs Movie:** Both are valuable, but if you're trying to understand reality, you watch the documentary. CursorFlow is the documentary of web testing.
84
84
 
85
+ ## 🎯 Pass-Through Architecture
86
+
87
+ CursorFlow doesn't limit you - it exposes the full power of Playwright:
88
+
89
+ **94+ Playwright actions available:**
90
+ ```bash
91
+ # Any Playwright Page method works
92
+ cursorflow test --actions '[
93
+ {"hover": ".menu"},
94
+ {"dblclick": ".editable"},
95
+ {"press": "Enter"},
96
+ {"drag_and_drop": {"source": ".item", "target": ".zone"}},
97
+ {"check": "#checkbox"},
98
+ {"evaluate": "window.scrollTo(0, 500)"}
99
+ ]'
100
+ ```
101
+
102
+ **Full configuration pass-through:**
103
+ ```json
104
+ {
105
+ "browser_config": {
106
+ "browser_launch_options": {"devtools": true, "channel": "chrome"}
107
+ },
108
+ "context_options": {
109
+ "color_scheme": "dark",
110
+ "geolocation": {"latitude": 40.7128, "longitude": -74.0060},
111
+ "timezone_id": "America/Los_Angeles"
112
+ }
113
+ }
114
+ ```
115
+
116
+ **Forward-compatible:** New Playwright features work immediately without CursorFlow updates.
117
+
118
+ **See:** [Playwright API Documentation](https://playwright.dev/python/docs/api/class-page)
119
+
120
+ ---
121
+
85
122
  ## 🚀 Complete Page Intelligence
86
123
 
87
- Every screenshot captures everything your AI needs to make intelligent decisions:
124
+ Every test captures everything needed for debugging:
88
125
 
89
126
  ### **📊 Comprehensive Data Collection**
90
- - **DOM**: All elements with 7 selector strategies each
91
- - **Network**: All requests, responses, and complete response bodies
92
- - **Console**: All logs, errors, and smart error correlation
93
- - **Performance**: Load times, memory usage, with reliability indicators
94
- - **Visual**: Screenshots with pixel-perfect comparisons and enhanced options
95
- - **Fonts**: Loading status, performance, and usage analysis
96
- - **Animations**: Active animations and transitions tracking
97
- - **Resources**: Complete resource loading analysis
98
- - **Storage**: localStorage, sessionStorage, cookies, IndexedDB state
127
+ - **DOM**: All elements with 7 selector strategies + event handlers
128
+ - **Network**: Requests, responses, and complete request/response bodies
129
+ - **Console**: All logs, errors, warnings - displayed prominently
130
+ - **JavaScript**: Global functions, variables, specific window objects
131
+ - **Storage**: localStorage, sessionStorage, cookies (sensitive data masked)
132
+ - **Forms**: All field values at capture time (passwords masked)
133
+ - **Performance**: Load times, memory usage, reliability indicators
134
+ - **Visual**: Screenshots with comprehensive page analysis
135
+ - **Sessions**: Save/restore browser state for authenticated testing
99
136
 
100
137
  ### **🔄 Hot Reload Intelligence**
101
138
  - **Framework auto-detection** (Vite, Webpack, Next.js, Parcel, Laravel Mix)
@@ -148,20 +185,38 @@ playwright install chromium
148
185
  ```bash
149
186
  cd /path/to/your/project
150
187
  cursorflow install-rules
188
+
189
+ # Or skip prompts for automation/CI
190
+ cursorflow install-rules --yes
151
191
  ```
152
192
 
153
193
  This creates:
154
194
  - `.cursor/rules/` - Cursor AI integration rules
155
- - `cursorflow-config.json` - Project-specific configuration
195
+ - `.cursorflow/config.json` - Project-specific configuration
156
196
  - `.cursorflow/` - Artifacts and session storage
157
197
  - `.gitignore` entries for CursorFlow artifacts
158
198
 
159
199
  ### Step 3: Start Testing
200
+
201
+ **Simple page capture:**
160
202
  ```bash
161
- # Test real application behavior
162
- cursorflow test --base-url http://localhost:3000 --path "/dashboard"
203
+ cursorflow test --base-url http://localhost:3000 --path /dashboard
204
+ ```
163
205
 
164
- # Get complete intelligence with custom actions
206
+ **Interactive testing with inline actions:**
207
+ ```bash
208
+ cursorflow test --base-url http://localhost:3000 \
209
+ --path /messages \
210
+ --wait-for ".message-item" \
211
+ --hover ".message-item:first-child" \
212
+ --click ".message-item:first-child" \
213
+ --screenshot "clicked" \
214
+ --show-console \
215
+ --open-trace
216
+ ```
217
+
218
+ **Custom actions with JSON:**
219
+ ```bash
165
220
  cursorflow test --base-url http://localhost:3000 --actions '[
166
221
  {"navigate": "/login"},
167
222
  {"fill": {"selector": "#email", "value": "test@example.com"}},