wup 0.2.16__tar.gz → 0.2.21__tar.gz

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.
Files changed (47) hide show
  1. {wup-0.2.16/wup.egg-info → wup-0.2.21}/PKG-INFO +107 -22
  2. {wup-0.2.16 → wup-0.2.21}/README.md +106 -21
  3. {wup-0.2.16 → wup-0.2.21}/pyproject.toml +1 -1
  4. {wup-0.2.16 → wup-0.2.21}/wup/__init__.py +1 -1
  5. wup-0.2.21/wup/_ast_detector.py +124 -0
  6. wup-0.2.21/wup/_hash_detector.py +72 -0
  7. wup-0.2.21/wup/_yaml_detector.py +128 -0
  8. wup-0.2.21/wup/anomaly_detector.py +175 -0
  9. wup-0.2.21/wup/anomaly_models.py +35 -0
  10. wup-0.2.21/wup/assistant.py +694 -0
  11. {wup-0.2.16 → wup-0.2.21}/wup/cli.py +28 -0
  12. {wup-0.2.16 → wup-0.2.21}/wup/config.py +60 -5
  13. {wup-0.2.16 → wup-0.2.21}/wup/core.py +20 -34
  14. {wup-0.2.16 → wup-0.2.21}/wup/models/__init__.py +21 -8
  15. {wup-0.2.16 → wup-0.2.21}/wup/models/config.py +29 -3
  16. {wup-0.2.16 → wup-0.2.21}/wup/testql_watcher.py +115 -80
  17. {wup-0.2.16 → wup-0.2.21}/wup/visual_diff.py +64 -9
  18. {wup-0.2.16 → wup-0.2.21}/wup/web_client.py +4 -4
  19. {wup-0.2.16 → wup-0.2.21/wup.egg-info}/PKG-INFO +107 -22
  20. {wup-0.2.16 → wup-0.2.21}/wup.egg-info/SOURCES.txt +6 -14
  21. wup-0.2.21/wup.egg-info/top_level.txt +1 -0
  22. wup-0.2.16/wup-web/tests/__init__.py +0 -0
  23. wup-0.2.16/wup-web/tests/conftest.py +0 -23
  24. wup-0.2.16/wup-web/tests/test_dashboard.py +0 -35
  25. wup-0.2.16/wup-web/tests/test_drivers.py +0 -50
  26. wup-0.2.16/wup-web/tests/test_events.py +0 -95
  27. wup-0.2.16/wup-web/wup_web/__init__.py +0 -7
  28. wup-0.2.16/wup-web/wup_web/__main__.py +0 -21
  29. wup-0.2.16/wup-web/wup_web/main.py +0 -44
  30. wup-0.2.16/wup-web/wup_web/models.py +0 -59
  31. wup-0.2.16/wup-web/wup_web/routers/__init__.py +0 -1
  32. wup-0.2.16/wup-web/wup_web/routers/dashboard.py +0 -24
  33. wup-0.2.16/wup-web/wup_web/routers/drivers.py +0 -129
  34. wup-0.2.16/wup-web/wup_web/routers/events.py +0 -48
  35. wup-0.2.16/wup-web/wup_web/storage.py +0 -110
  36. wup-0.2.16/wup.egg-info/top_level.txt +0 -2
  37. {wup-0.2.16 → wup-0.2.21}/LICENSE +0 -0
  38. {wup-0.2.16 → wup-0.2.21}/setup.cfg +0 -0
  39. {wup-0.2.16 → wup-0.2.21}/tests/test_e2e.py +0 -0
  40. {wup-0.2.16 → wup-0.2.21}/tests/test_testql_watcher.py +0 -0
  41. {wup-0.2.16 → wup-0.2.21}/tests/test_web_client.py +0 -0
  42. {wup-0.2.16 → wup-0.2.21}/tests/test_wup.py +0 -0
  43. {wup-0.2.16 → wup-0.2.21}/wup/dependency_mapper.py +0 -0
  44. {wup-0.2.16 → wup-0.2.21}/wup/testql_discovery.py +0 -0
  45. {wup-0.2.16 → wup-0.2.21}/wup.egg-info/dependency_links.txt +0 -0
  46. {wup-0.2.16 → wup-0.2.21}/wup.egg-info/entry_points.txt +0 -0
  47. {wup-0.2.16 → wup-0.2.21}/wup.egg-info/requires.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wup
3
- Version: 0.2.16
3
+ Version: 0.2.21
4
4
  Summary: WUP (What's Up) - Intelligent file watcher for regression testing in large projects
5
5
  Author-email: Tom Sapletta <tom@sapletta.com>
6
6
  License: Apache-2.0
@@ -29,17 +29,17 @@ Dynamic: license-file
29
29
 
30
30
  ## AI Cost Tracking
31
31
 
32
- ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.2.16-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
33
- ![AI Cost](https://img.shields.io/badge/AI%20Cost-$2.55-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-4.5h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
32
+ ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.2.21-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
33
+ ![AI Cost](https://img.shields.io/badge/AI%20Cost-$4.80-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-9.1h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
34
34
 
35
- - 🤖 **LLM usage:** $2.5500 (17 commits)
36
- - 👤 **Human dev:** ~$445 (4.5h @ $100/h, 30min dedup)
35
+ - 🤖 **LLM usage:** $4.8000 (32 commits)
36
+ - 👤 **Human dev:** ~$913 (9.1h @ $100/h, 30min dedup)
37
37
 
38
- Generated on 2026-04-29 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
38
+ Generated on 2026-05-01 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
39
39
 
40
40
  ---
41
41
 
42
- ![PyPI](https://img.shields.io/badge/pypi-wup-blue) ![Version](https://img.shields.io/badge/version-0.2.16-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
42
+ ![PyPI](https://img.shields.io/badge/pypi-wup-blue) ![Version](https://img.shields.io/badge/version-0.2.21-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
43
43
 
44
44
  **WUP (What's Up)** - Intelligent file watcher for regression testing in large projects.
45
45
 
@@ -74,16 +74,19 @@ pip install -e ".[dev]"
74
74
  ## Quick Start
75
75
 
76
76
  ```bash
77
- # 1. Initialize configuration (optional)
78
- wup init
77
+ # 1. Interactive configuration (recommended)
78
+ wup assistant
79
+
80
+ # 2. Or quick auto-setup
81
+ wup assistant --quick --template fastapi
79
82
 
80
- # 2. Build dependency map (one-time setup)
83
+ # 3. Build dependency map (one-time setup)
81
84
  wup map-deps ./my-project
82
85
 
83
- # 3. Start watching for changes
86
+ # 4. Start watching for changes
84
87
  wup watch ./my-project
85
88
 
86
- # 4. Start with live dashboard
89
+ # 5. Start with live dashboard
87
90
  wup watch ./my-project --dashboard
88
91
  ```
89
92
 
@@ -186,6 +189,16 @@ Full regression: 15s test → 15% CPU spike
186
189
 
187
190
  ## Configuration
188
191
 
192
+ ### Service Types
193
+
194
+ WUP supports three service types for coincidence detection and intelligent testing:
195
+
196
+ - **web** - HTTP/API services (FastAPI, Flask, Django, Express.js, etc.)
197
+ - **shell** - CLI tools, scripts, and command-line services
198
+ - **auto** - Automatic detection (default)
199
+
200
+ Coincidence detection allows WUP to identify related services. For example, if you have `users-web` and `users-shell`, WUP will detect they share the same domain and test both when relevant files change.
201
+
189
202
  ### wup.yaml Configuration File
190
203
 
191
204
  WUP supports declarative configuration via `wup.yaml` (or `.wup.yaml`) in your project root. This allows you to define watch paths, service-specific settings, and test strategies.
@@ -196,6 +209,11 @@ Generate a default configuration:
196
209
  wup init
197
210
  ```
198
211
 
212
+ The generated `wup.yaml` includes:
213
+ - **Metadata header**: Version, generation date, documentation links
214
+ - **Dependencies info**: WUP version and optional wupbro dashboard
215
+ - **Quick start guide**: Common commands to get started
216
+
199
217
  Example `wup.yaml`:
200
218
 
201
219
  ```yaml
@@ -285,6 +303,26 @@ export WUP_CPU_THROTTLE=0.5
285
303
  export WUP_DEBOUNCE=3
286
304
  ```
287
305
 
306
+ Full list of supported variables (see `.env.example`):
307
+
308
+ | Variable | Default | Description |
309
+ |----------|---------|-------------|
310
+ | `WUP_CPU_THROTTLE` | — | CPU usage threshold (0.0-1.0) |
311
+ | `WUP_DEBOUNCE` | — | Debounce time in seconds |
312
+ | `WUPBRO_ENDPOINT` | — | wupbro backend URL |
313
+ | `WUP_BASE_URL` | — | Base URL for visual diff page scanning |
314
+ | `OPENROUTER_API_KEY` | *(not set)* | Required for LLM features (https://openrouter.ai/keys) |
315
+ | `LLM_MODEL` | `openrouter/qwen/qwen3-coder-next` | LLM model for assistant |
316
+ | `PFIX_AUTO_APPLY` | `true` | Apply fixes without asking |
317
+ | `PFIX_AUTO_INSTALL_DEPS` | `true` | Auto pip/uv install missing deps |
318
+ | `PFIX_AUTO_RESTART` | `false` | Restart process after fix |
319
+ | `PFIX_MAX_RETRIES` | `3` | Max fix retries |
320
+ | `PFIX_DRY_RUN` | `false` | Dry-run mode (no changes written) |
321
+ | `PFIX_ENABLED` | `true` | Enable/disable pfix |
322
+ | `PFIX_GIT_COMMIT` | `false` | Auto-commit fixes |
323
+ | `PFIX_GIT_PREFIX` | `pfix:` | Commit message prefix |
324
+ | `PFIX_CREATE_BACKUPS` | `false` | Create .pfix_backups/ directory |
325
+
288
326
  ## Visual DOM Diff
289
327
 
290
328
  WUP optionally scans configured pages with Playwright after each successful quick test, compares the DOM structure to the previous snapshot, and reports significant changes.
@@ -333,15 +371,15 @@ Visible in `wup status` as a "Visual DOM diffs" section.
333
371
 
334
372
  If Playwright is not installed, the visual diff module logs a warning and skips scanning — it does **not** break the watcher.
335
373
 
336
- ## Web Dashboard (wup-web)
374
+ ## Web Dashboard (wupbro)
337
375
 
338
376
  Optional FastAPI backend that receives events from WUP agents and renders a live dashboard.
339
377
 
340
378
  ### Run
341
379
 
342
380
  ```bash
343
- pip install -e wup-web/
344
- wup-web --reload --port 8000
381
+ pip install -e wupbro/
382
+ wupbro --reload --port 8000
345
383
  ```
346
384
 
347
385
  Open <http://localhost:8000/> to see regressions, passes, anomalies, visual diffs, and health transitions in real time.
@@ -359,12 +397,12 @@ web:
359
397
  Or via env:
360
398
 
361
399
  ```bash
362
- export WUP_WEB_ENDPOINT=http://localhost:8000
400
+ export WUPBRO_ENDPOINT=http://localhost:8000
363
401
  ```
364
402
 
365
403
  The agent fire-and-forgets `REGRESSION`, `PASS`, `ANOMALY`, `VISUAL_DIFF`, and `HEALTH_TRANSITION` events. Network errors never break the watcher (soft-fail).
366
404
 
367
- See `wup-web/README.md` for full API reference and driver endpoints (DOM diff, browserless, anomaly).
405
+ See `wupbro/README.md` for full API reference and driver endpoints (DOM diff, browserless, anomaly).
368
406
 
369
407
  ## Project Structure
370
408
 
@@ -372,19 +410,21 @@ See `wup-web/README.md` for full API reference and driver endpoints (DOM diff, b
372
410
  wup/
373
411
  ├── wup/
374
412
  │ ├── __init__.py # Package exports
375
- │ ├── cli.py # CLI: watch, map-deps, status, init, testql-endpoints
413
+ │ ├── anomaly_detector.py # AnomalyDetector: hash, YAML structure, AST diff
414
+ │ ├── assistant.py # WupAssistant: interactive configuration wizard
415
+ │ ├── cli.py # CLI: watch, map-deps, status, init, testql-endpoints, assistant, version
376
416
  │ ├── config.py # Config loading/saving + .wup.env support
377
417
  │ ├── core.py # WupWatcher: detection, inference, scheduling
378
418
  │ ├── dependency_mapper.py # DependencyMapper: codebase → deps.json
379
419
  │ ├── testql_discovery.py # TestQLEndpointDiscovery: scenario parsing
380
420
  │ ├── testql_watcher.py # TestQLWatcher: scenario runner + health tracking
381
421
  │ ├── visual_diff.py # VisualDiffer: Playwright DOM snapshot + diff engine
382
- │ ├── web_client.py # WebClient: async HTTP event sink → wup-web
422
+ │ ├── web_client.py # WebClient: async HTTP event sink → wupbro
383
423
  │ └── models/
384
424
  │ ├── __init__.py
385
- │ └── config.py # Dataclasses: WupConfig, VisualDiffConfig, WebConfig...
386
- ├── wup-web/ # Optional FastAPI dashboard (separate package)
387
- │ ├── wup_web/
425
+ │ └── config.py # Dataclasses: WupConfig, ServiceConfig, WatchConfig, TestStrategyConfig, TestQLConfig, VisualDiffConfig, WebConfig, AnomalyDetectionConfig...
426
+ ├── wupbro/ # Optional FastAPI dashboard (separate package)
427
+ │ ├── wupbro/
388
428
  │ │ ├── main.py # FastAPI app
389
429
  │ │ ├── routers/ # events, drivers, dashboard
390
430
  │ │ ├── storage.py # EventStore (in-memory + JSONL)
@@ -436,6 +476,18 @@ python3 examples/visual_diff_demo.py http://localhost:8100/health
436
476
 
437
477
  # TestQL + visual diff integration
438
478
  python3 examples/testql_integration.py /path/to/project
479
+
480
+ # Monorepo analysis (c2004-style large projects)
481
+ python3 examples/c2004_monorepo_demo.py /path/to/monorepo
482
+
483
+ # CI/CD integration patterns
484
+ python3 examples/ci_cd_integration.py
485
+
486
+ # Generate GitHub Actions workflow
487
+ python3 examples/ci_cd_integration.py --generate-github-actions
488
+
489
+ # Webhook notifications (Slack, Teams, Discord)
490
+ python3 examples/webhook_notifications.py
439
491
  ```
440
492
 
441
493
  ### Building & Publishing
@@ -444,6 +496,39 @@ python3 examples/testql_integration.py /path/to/project
444
496
  python -m build
445
497
  ```
446
498
 
499
+ ## Real-World Testing
500
+
501
+ WUP has been tested on production-scale projects:
502
+
503
+ - **c2004 Project** (maskservice/c2004): Large IoT platform with 21+ connect-* modules
504
+ - 29 services auto-detected by assistant
505
+ - 100+ YAML configuration files monitored
506
+ - Anomaly detection: 0.06s for 5 config files (~1ms/file)
507
+ - Framework: Custom Python/FastAPI hybrid
508
+
509
+ ## Documentation
510
+
511
+ Comprehensive documentation is available in the `docs/` directory:
512
+
513
+ - **[Configuration Assistant](docs/WUP_ASSISTANT.md)** - Interactive setup guide for `wup.yaml`
514
+ - `wup assistant` - Interactive configuration wizard
515
+ - Auto-detects framework and services
516
+ - Intelligent suggestions and validation
517
+
518
+ - **[Anomaly Detection](docs/ANOMALY_DETECTION.md)** - Fast alternatives to Playwright
519
+ - Hash-based change detection (~1ms per file)
520
+ - YAML structure analysis
521
+ - Python AST diff for API changes
522
+ - Configure with `anomaly_detection:` in wup.yaml
523
+
524
+ - **[Browser Notifications](docs/NOTIFICATIONS.md)** - Real-time alerts in wupbro
525
+ - 7 notification types (regressions, status changes, recoveries)
526
+ - Configurable per-type with cooldown
527
+ - Server-Sent Events for instant delivery
528
+ - Browser Notifications API integration
529
+
530
+ - **[TestQL Integration](docs/TESTQL_INTEGRATION.md)** - TestQL scenario support
531
+
447
532
  ## License
448
533
 
449
534
  Licensed under Apache-2.0.
@@ -3,17 +3,17 @@
3
3
 
4
4
  ## AI Cost Tracking
5
5
 
6
- ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.2.16-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
7
- ![AI Cost](https://img.shields.io/badge/AI%20Cost-$2.55-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-4.5h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
6
+ ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.2.21-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
7
+ ![AI Cost](https://img.shields.io/badge/AI%20Cost-$4.80-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-9.1h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
8
8
 
9
- - 🤖 **LLM usage:** $2.5500 (17 commits)
10
- - 👤 **Human dev:** ~$445 (4.5h @ $100/h, 30min dedup)
9
+ - 🤖 **LLM usage:** $4.8000 (32 commits)
10
+ - 👤 **Human dev:** ~$913 (9.1h @ $100/h, 30min dedup)
11
11
 
12
- Generated on 2026-04-29 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
12
+ Generated on 2026-05-01 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
13
13
 
14
14
  ---
15
15
 
16
- ![PyPI](https://img.shields.io/badge/pypi-wup-blue) ![Version](https://img.shields.io/badge/version-0.2.16-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
16
+ ![PyPI](https://img.shields.io/badge/pypi-wup-blue) ![Version](https://img.shields.io/badge/version-0.2.21-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
17
17
 
18
18
  **WUP (What's Up)** - Intelligent file watcher for regression testing in large projects.
19
19
 
@@ -48,16 +48,19 @@ pip install -e ".[dev]"
48
48
  ## Quick Start
49
49
 
50
50
  ```bash
51
- # 1. Initialize configuration (optional)
52
- wup init
51
+ # 1. Interactive configuration (recommended)
52
+ wup assistant
53
+
54
+ # 2. Or quick auto-setup
55
+ wup assistant --quick --template fastapi
53
56
 
54
- # 2. Build dependency map (one-time setup)
57
+ # 3. Build dependency map (one-time setup)
55
58
  wup map-deps ./my-project
56
59
 
57
- # 3. Start watching for changes
60
+ # 4. Start watching for changes
58
61
  wup watch ./my-project
59
62
 
60
- # 4. Start with live dashboard
63
+ # 5. Start with live dashboard
61
64
  wup watch ./my-project --dashboard
62
65
  ```
63
66
 
@@ -160,6 +163,16 @@ Full regression: 15s test → 15% CPU spike
160
163
 
161
164
  ## Configuration
162
165
 
166
+ ### Service Types
167
+
168
+ WUP supports three service types for coincidence detection and intelligent testing:
169
+
170
+ - **web** - HTTP/API services (FastAPI, Flask, Django, Express.js, etc.)
171
+ - **shell** - CLI tools, scripts, and command-line services
172
+ - **auto** - Automatic detection (default)
173
+
174
+ Coincidence detection allows WUP to identify related services. For example, if you have `users-web` and `users-shell`, WUP will detect they share the same domain and test both when relevant files change.
175
+
163
176
  ### wup.yaml Configuration File
164
177
 
165
178
  WUP supports declarative configuration via `wup.yaml` (or `.wup.yaml`) in your project root. This allows you to define watch paths, service-specific settings, and test strategies.
@@ -170,6 +183,11 @@ Generate a default configuration:
170
183
  wup init
171
184
  ```
172
185
 
186
+ The generated `wup.yaml` includes:
187
+ - **Metadata header**: Version, generation date, documentation links
188
+ - **Dependencies info**: WUP version and optional wupbro dashboard
189
+ - **Quick start guide**: Common commands to get started
190
+
173
191
  Example `wup.yaml`:
174
192
 
175
193
  ```yaml
@@ -259,6 +277,26 @@ export WUP_CPU_THROTTLE=0.5
259
277
  export WUP_DEBOUNCE=3
260
278
  ```
261
279
 
280
+ Full list of supported variables (see `.env.example`):
281
+
282
+ | Variable | Default | Description |
283
+ |----------|---------|-------------|
284
+ | `WUP_CPU_THROTTLE` | — | CPU usage threshold (0.0-1.0) |
285
+ | `WUP_DEBOUNCE` | — | Debounce time in seconds |
286
+ | `WUPBRO_ENDPOINT` | — | wupbro backend URL |
287
+ | `WUP_BASE_URL` | — | Base URL for visual diff page scanning |
288
+ | `OPENROUTER_API_KEY` | *(not set)* | Required for LLM features (https://openrouter.ai/keys) |
289
+ | `LLM_MODEL` | `openrouter/qwen/qwen3-coder-next` | LLM model for assistant |
290
+ | `PFIX_AUTO_APPLY` | `true` | Apply fixes without asking |
291
+ | `PFIX_AUTO_INSTALL_DEPS` | `true` | Auto pip/uv install missing deps |
292
+ | `PFIX_AUTO_RESTART` | `false` | Restart process after fix |
293
+ | `PFIX_MAX_RETRIES` | `3` | Max fix retries |
294
+ | `PFIX_DRY_RUN` | `false` | Dry-run mode (no changes written) |
295
+ | `PFIX_ENABLED` | `true` | Enable/disable pfix |
296
+ | `PFIX_GIT_COMMIT` | `false` | Auto-commit fixes |
297
+ | `PFIX_GIT_PREFIX` | `pfix:` | Commit message prefix |
298
+ | `PFIX_CREATE_BACKUPS` | `false` | Create .pfix_backups/ directory |
299
+
262
300
  ## Visual DOM Diff
263
301
 
264
302
  WUP optionally scans configured pages with Playwright after each successful quick test, compares the DOM structure to the previous snapshot, and reports significant changes.
@@ -307,15 +345,15 @@ Visible in `wup status` as a "Visual DOM diffs" section.
307
345
 
308
346
  If Playwright is not installed, the visual diff module logs a warning and skips scanning — it does **not** break the watcher.
309
347
 
310
- ## Web Dashboard (wup-web)
348
+ ## Web Dashboard (wupbro)
311
349
 
312
350
  Optional FastAPI backend that receives events from WUP agents and renders a live dashboard.
313
351
 
314
352
  ### Run
315
353
 
316
354
  ```bash
317
- pip install -e wup-web/
318
- wup-web --reload --port 8000
355
+ pip install -e wupbro/
356
+ wupbro --reload --port 8000
319
357
  ```
320
358
 
321
359
  Open <http://localhost:8000/> to see regressions, passes, anomalies, visual diffs, and health transitions in real time.
@@ -333,12 +371,12 @@ web:
333
371
  Or via env:
334
372
 
335
373
  ```bash
336
- export WUP_WEB_ENDPOINT=http://localhost:8000
374
+ export WUPBRO_ENDPOINT=http://localhost:8000
337
375
  ```
338
376
 
339
377
  The agent fire-and-forgets `REGRESSION`, `PASS`, `ANOMALY`, `VISUAL_DIFF`, and `HEALTH_TRANSITION` events. Network errors never break the watcher (soft-fail).
340
378
 
341
- See `wup-web/README.md` for full API reference and driver endpoints (DOM diff, browserless, anomaly).
379
+ See `wupbro/README.md` for full API reference and driver endpoints (DOM diff, browserless, anomaly).
342
380
 
343
381
  ## Project Structure
344
382
 
@@ -346,19 +384,21 @@ See `wup-web/README.md` for full API reference and driver endpoints (DOM diff, b
346
384
  wup/
347
385
  ├── wup/
348
386
  │ ├── __init__.py # Package exports
349
- │ ├── cli.py # CLI: watch, map-deps, status, init, testql-endpoints
387
+ │ ├── anomaly_detector.py # AnomalyDetector: hash, YAML structure, AST diff
388
+ │ ├── assistant.py # WupAssistant: interactive configuration wizard
389
+ │ ├── cli.py # CLI: watch, map-deps, status, init, testql-endpoints, assistant, version
350
390
  │ ├── config.py # Config loading/saving + .wup.env support
351
391
  │ ├── core.py # WupWatcher: detection, inference, scheduling
352
392
  │ ├── dependency_mapper.py # DependencyMapper: codebase → deps.json
353
393
  │ ├── testql_discovery.py # TestQLEndpointDiscovery: scenario parsing
354
394
  │ ├── testql_watcher.py # TestQLWatcher: scenario runner + health tracking
355
395
  │ ├── visual_diff.py # VisualDiffer: Playwright DOM snapshot + diff engine
356
- │ ├── web_client.py # WebClient: async HTTP event sink → wup-web
396
+ │ ├── web_client.py # WebClient: async HTTP event sink → wupbro
357
397
  │ └── models/
358
398
  │ ├── __init__.py
359
- │ └── config.py # Dataclasses: WupConfig, VisualDiffConfig, WebConfig...
360
- ├── wup-web/ # Optional FastAPI dashboard (separate package)
361
- │ ├── wup_web/
399
+ │ └── config.py # Dataclasses: WupConfig, ServiceConfig, WatchConfig, TestStrategyConfig, TestQLConfig, VisualDiffConfig, WebConfig, AnomalyDetectionConfig...
400
+ ├── wupbro/ # Optional FastAPI dashboard (separate package)
401
+ │ ├── wupbro/
362
402
  │ │ ├── main.py # FastAPI app
363
403
  │ │ ├── routers/ # events, drivers, dashboard
364
404
  │ │ ├── storage.py # EventStore (in-memory + JSONL)
@@ -410,6 +450,18 @@ python3 examples/visual_diff_demo.py http://localhost:8100/health
410
450
 
411
451
  # TestQL + visual diff integration
412
452
  python3 examples/testql_integration.py /path/to/project
453
+
454
+ # Monorepo analysis (c2004-style large projects)
455
+ python3 examples/c2004_monorepo_demo.py /path/to/monorepo
456
+
457
+ # CI/CD integration patterns
458
+ python3 examples/ci_cd_integration.py
459
+
460
+ # Generate GitHub Actions workflow
461
+ python3 examples/ci_cd_integration.py --generate-github-actions
462
+
463
+ # Webhook notifications (Slack, Teams, Discord)
464
+ python3 examples/webhook_notifications.py
413
465
  ```
414
466
 
415
467
  ### Building & Publishing
@@ -418,6 +470,39 @@ python3 examples/testql_integration.py /path/to/project
418
470
  python -m build
419
471
  ```
420
472
 
473
+ ## Real-World Testing
474
+
475
+ WUP has been tested on production-scale projects:
476
+
477
+ - **c2004 Project** (maskservice/c2004): Large IoT platform with 21+ connect-* modules
478
+ - 29 services auto-detected by assistant
479
+ - 100+ YAML configuration files monitored
480
+ - Anomaly detection: 0.06s for 5 config files (~1ms/file)
481
+ - Framework: Custom Python/FastAPI hybrid
482
+
483
+ ## Documentation
484
+
485
+ Comprehensive documentation is available in the `docs/` directory:
486
+
487
+ - **[Configuration Assistant](docs/WUP_ASSISTANT.md)** - Interactive setup guide for `wup.yaml`
488
+ - `wup assistant` - Interactive configuration wizard
489
+ - Auto-detects framework and services
490
+ - Intelligent suggestions and validation
491
+
492
+ - **[Anomaly Detection](docs/ANOMALY_DETECTION.md)** - Fast alternatives to Playwright
493
+ - Hash-based change detection (~1ms per file)
494
+ - YAML structure analysis
495
+ - Python AST diff for API changes
496
+ - Configure with `anomaly_detection:` in wup.yaml
497
+
498
+ - **[Browser Notifications](docs/NOTIFICATIONS.md)** - Real-time alerts in wupbro
499
+ - 7 notification types (regressions, status changes, recoveries)
500
+ - Configurable per-type with cooldown
501
+ - Server-Sent Events for instant delivery
502
+ - Browser Notifications API integration
503
+
504
+ - **[TestQL Integration](docs/TESTQL_INTEGRATION.md)** - TestQL scenario support
505
+
421
506
  ## License
422
507
 
423
508
  Licensed under Apache-2.0.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "wup"
7
- version = "0.2.16"
7
+ version = "0.2.21"
8
8
  description = "WUP (What's Up) - Intelligent file watcher for regression testing in large projects"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -7,7 +7,7 @@ WUP monitors file changes and runs intelligent regression tests using a 3-layer
7
7
  3. Detail Layer: Full tests with blame reports (only on failure)
8
8
  """
9
9
 
10
- __version__ = "0.2.16"
10
+ __version__ = "0.2.21"
11
11
  __author__ = "Tom Sapletta"
12
12
 
13
13
  from .config import load_config, save_config, get_default_config
@@ -0,0 +1,124 @@
1
+ """Python AST-based anomaly detection."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import ast
6
+ import json
7
+ from pathlib import Path
8
+ from typing import Dict, List, Optional
9
+
10
+ from .anomaly_models import AnomalyResult
11
+
12
+
13
+ class ASTDetector:
14
+ """Detect changes in Python files using AST comparison."""
15
+
16
+ def __init__(self, snapshot_dir: Path):
17
+ self.snapshot_dir = snapshot_dir / 'ast_snapshots'
18
+ self.snapshot_dir.mkdir(parents=True, exist_ok=True)
19
+
20
+ @staticmethod
21
+ def _collect_import(node: ast.Import) -> List[str]:
22
+ return [f"import {alias.name}" for alias in node.names]
23
+
24
+ @staticmethod
25
+ def _collect_import_from(node: ast.ImportFrom) -> str:
26
+ module = node.module or ''
27
+ names = ', '.join(a.name for a in node.names)
28
+ return f"from {module} import {names}"
29
+
30
+ @staticmethod
31
+ def _collect_class(node: ast.ClassDef) -> Dict:
32
+ methods = [n.name for n in node.body if isinstance(n, ast.FunctionDef)]
33
+ bases = [ast.unparse(b) for b in node.bases] if hasattr(ast, 'unparse') else []
34
+ return {'name': node.name, 'methods': methods, 'bases': bases}
35
+
36
+ @staticmethod
37
+ def _collect_function(node: ast.FunctionDef) -> Dict:
38
+ return {'name': node.name, 'args': len(node.args.args),
39
+ 'decorators': len(node.decorator_list)}
40
+
41
+ def _extract_ast_info(self, tree: ast.AST) -> Dict:
42
+ info: Dict = {'imports': [], 'classes': [], 'functions': [], 'top_level': []}
43
+ _handlers = {
44
+ ast.Import: lambda n: info['imports'].extend(self._collect_import(n)),
45
+ ast.ImportFrom: lambda n: info['imports'].append(self._collect_import_from(n)),
46
+ ast.ClassDef: lambda n: info['classes'].append(self._collect_class(n)),
47
+ ast.FunctionDef: lambda n: info['functions'].append(self._collect_function(n)),
48
+ }
49
+ for node in ast.iter_child_nodes(tree):
50
+ handler = _handlers.get(type(node))
51
+ if handler:
52
+ handler(node)
53
+ elif isinstance(node, ast.Assign):
54
+ for target in node.targets:
55
+ if isinstance(target, ast.Name):
56
+ info['top_level'].append(target.id)
57
+ return info
58
+
59
+ def _snapshot_path(self, file_path: Path) -> Path:
60
+ rel_path = str(file_path).replace('/', '_').replace('\\', '_')
61
+ return self.snapshot_dir / f"{rel_path}.ast.json"
62
+
63
+ def _compute_changes(self, old_info: Dict, new_info: Dict) -> List[str]:
64
+ changes: List[str] = []
65
+ old_classes = {c['name']: c for c in old_info.get('classes', [])}
66
+ new_classes = {c['name']: c for c in new_info.get('classes', [])}
67
+
68
+ for name in set(old_classes) | set(new_classes):
69
+ if name not in new_classes:
70
+ changes.append(f"Klasa usunięta: {name}")
71
+ elif name not in old_classes:
72
+ changes.append(f"Nowa klasa: {name}")
73
+ elif old_classes[name] != new_classes[name]:
74
+ changes.append(f"Klasa zmieniona: {name}")
75
+
76
+ old_funcs = {f['name'] for f in old_info.get('functions', [])}
77
+ new_funcs = {f['name'] for f in new_info.get('functions', [])}
78
+ for name in old_funcs - new_funcs:
79
+ changes.append(f"Funkcja usunięta: {name}")
80
+ for name in new_funcs - old_funcs:
81
+ changes.append(f"Nowa funkcja: {name}")
82
+
83
+ return changes
84
+
85
+ def detect(self, file_path: Path) -> Optional[AnomalyResult]:
86
+ """Detect changes in Python file structure."""
87
+ if not str(file_path).endswith('.py'):
88
+ return None
89
+
90
+ try:
91
+ content = file_path.read_text(encoding='utf-8')
92
+ tree = ast.parse(content)
93
+ new_info = self._extract_ast_info(tree)
94
+ snap_path = self._snapshot_path(file_path)
95
+
96
+ if snap_path.exists():
97
+ old_info = json.loads(snap_path.read_text())
98
+ changes = self._compute_changes(old_info, new_info)
99
+ if changes:
100
+ snap_path.write_text(json.dumps(new_info, indent=2))
101
+ return AnomalyResult(
102
+ detector='ast',
103
+ file_path=str(file_path),
104
+ anomaly_type='changed',
105
+ severity='high',
106
+ message=f"Struktura Python zmieniona ({len(changes)} zmian)",
107
+ details={'changes': changes[:10]},
108
+ suggestions=["Przejrzyj zmiany w API przed deploymentem"],
109
+ )
110
+ else:
111
+ snap_path.write_text(json.dumps(new_info, indent=2))
112
+
113
+ return None
114
+
115
+ except SyntaxError as e:
116
+ return AnomalyResult(
117
+ detector='ast',
118
+ file_path=str(file_path),
119
+ anomaly_type='error',
120
+ severity='critical',
121
+ message=f"Błąd składni Python: {e}",
122
+ )
123
+ except Exception:
124
+ return None
@@ -0,0 +1,72 @@
1
+ """Fast hash-based anomaly detection."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ from pathlib import Path
7
+ from typing import Optional
8
+
9
+ from .anomaly_models import AnomalyResult
10
+
11
+
12
+ class HashDetector:
13
+ """Fast anomaly detection using file hashes."""
14
+
15
+ def __init__(self, snapshot_dir: Path):
16
+ self.snapshot_dir = snapshot_dir / 'hash_snapshots'
17
+ self.snapshot_dir.mkdir(parents=True, exist_ok=True)
18
+
19
+ def _compute_hash(self, content: str) -> str:
20
+ return hashlib.sha256(content.encode('utf-8')).hexdigest()[:16]
21
+
22
+ def _snapshot_path(self, file_path: Path) -> Path:
23
+ rel_path = str(file_path).replace('/', '_').replace('\\', '_')
24
+ return self.snapshot_dir / f"{rel_path}.hash"
25
+
26
+ def detect(self, file_path: Path) -> Optional[AnomalyResult]:
27
+ """Detect changes using hash comparison."""
28
+ try:
29
+ if not file_path.exists():
30
+ return None
31
+
32
+ content = file_path.read_text(encoding='utf-8')
33
+ current_hash = self._compute_hash(content)
34
+ snap_path = self._snapshot_path(file_path)
35
+
36
+ if snap_path.exists():
37
+ old_hash = snap_path.read_text().strip()
38
+ if old_hash != current_hash:
39
+ snap_path.write_text(current_hash)
40
+ return AnomalyResult(
41
+ detector='hash',
42
+ file_path=str(file_path),
43
+ anomaly_type='changed',
44
+ severity='medium',
45
+ message=f"Plik zmieniony (hash: {old_hash[:8]} → {current_hash[:8]})",
46
+ details={
47
+ 'old_hash': old_hash,
48
+ 'new_hash': current_hash,
49
+ 'file_size': len(content),
50
+ },
51
+ )
52
+ else:
53
+ snap_path.write_text(current_hash)
54
+ return AnomalyResult(
55
+ detector='hash',
56
+ file_path=str(file_path),
57
+ anomaly_type='added',
58
+ severity='low',
59
+ message=f"Nowy plik wykryty (hash: {current_hash[:8]})",
60
+ details={'new_hash': current_hash},
61
+ )
62
+
63
+ return None
64
+
65
+ except Exception as e:
66
+ return AnomalyResult(
67
+ detector='hash',
68
+ file_path=str(file_path),
69
+ anomaly_type='error',
70
+ severity='low',
71
+ message=f"Błąd hash detection: {e}",
72
+ )