wup 0.2.38__tar.gz → 0.2.40__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 (40) hide show
  1. {wup-0.2.38/wup.egg-info → wup-0.2.40}/PKG-INFO +5 -5
  2. {wup-0.2.38 → wup-0.2.40}/README.md +4 -4
  3. {wup-0.2.38 → wup-0.2.40}/pyproject.toml +1 -1
  4. {wup-0.2.38 → wup-0.2.40}/wup/__init__.py +1 -1
  5. {wup-0.2.38 → wup-0.2.40}/wup/cli.py +36 -0
  6. {wup-0.2.38 → wup-0.2.40}/wup/cli_config_generator.py +2 -2
  7. {wup-0.2.38 → wup-0.2.40}/wup/config.py +1 -1
  8. {wup-0.2.38 → wup-0.2.40}/wup/models/config.py +1 -1
  9. {wup-0.2.38 → wup-0.2.40}/wup/testql_watcher.py +54 -2
  10. {wup-0.2.38 → wup-0.2.40/wup.egg-info}/PKG-INFO +5 -5
  11. {wup-0.2.38 → wup-0.2.40}/LICENSE +0 -0
  12. {wup-0.2.38 → wup-0.2.40}/setup.cfg +0 -0
  13. {wup-0.2.38 → wup-0.2.40}/tests/test_e2e.py +0 -0
  14. {wup-0.2.38 → wup-0.2.40}/tests/test_monitoring_manifest.py +0 -0
  15. {wup-0.2.38 → wup-0.2.40}/tests/test_testql_monitor.py +0 -0
  16. {wup-0.2.38 → wup-0.2.40}/tests/test_testql_watcher.py +0 -0
  17. {wup-0.2.38 → wup-0.2.40}/tests/test_web_client.py +0 -0
  18. {wup-0.2.38 → wup-0.2.40}/tests/test_wup.py +0 -0
  19. {wup-0.2.38 → wup-0.2.40}/wup/_ast_detector.py +0 -0
  20. {wup-0.2.38 → wup-0.2.40}/wup/_hash_detector.py +0 -0
  21. {wup-0.2.38 → wup-0.2.40}/wup/_yaml_detector.py +0 -0
  22. {wup-0.2.38 → wup-0.2.40}/wup/anomaly_detector.py +0 -0
  23. {wup-0.2.38 → wup-0.2.40}/wup/anomaly_models.py +0 -0
  24. {wup-0.2.38 → wup-0.2.40}/wup/assistant.py +0 -0
  25. {wup-0.2.38 → wup-0.2.40}/wup/cli_scanner.py +0 -0
  26. {wup-0.2.38 → wup-0.2.40}/wup/core.py +0 -0
  27. {wup-0.2.38 → wup-0.2.40}/wup/dependency_mapper.py +0 -0
  28. {wup-0.2.38 → wup-0.2.40}/wup/models/__init__.py +0 -0
  29. {wup-0.2.38 → wup-0.2.40}/wup/monitoring_manifest.py +0 -0
  30. {wup-0.2.38 → wup-0.2.40}/wup/planfile_reporter.py +0 -0
  31. {wup-0.2.38 → wup-0.2.40}/wup/testql_cli_generator.py +0 -0
  32. {wup-0.2.38 → wup-0.2.40}/wup/testql_discovery.py +0 -0
  33. {wup-0.2.38 → wup-0.2.40}/wup/testql_monitor.py +0 -0
  34. {wup-0.2.38 → wup-0.2.40}/wup/visual_diff.py +0 -0
  35. {wup-0.2.38 → wup-0.2.40}/wup/web_client.py +0 -0
  36. {wup-0.2.38 → wup-0.2.40}/wup.egg-info/SOURCES.txt +0 -0
  37. {wup-0.2.38 → wup-0.2.40}/wup.egg-info/dependency_links.txt +0 -0
  38. {wup-0.2.38 → wup-0.2.40}/wup.egg-info/entry_points.txt +0 -0
  39. {wup-0.2.38 → wup-0.2.40}/wup.egg-info/requires.txt +0 -0
  40. {wup-0.2.38 → wup-0.2.40}/wup.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wup
3
- Version: 0.2.38
3
+ Version: 0.2.40
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-Expression: Apache-2.0
@@ -31,17 +31,17 @@ Dynamic: license-file
31
31
 
32
32
  ## AI Cost Tracking
33
33
 
34
- ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.2.38-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
35
- ![AI Cost](https://img.shields.io/badge/AI%20Cost-$2.99-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-20.5h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
34
+ ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.2.40-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
35
+ ![AI Cost](https://img.shields.io/badge/AI%20Cost-$3.00-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-20.5h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
36
36
 
37
- - 🤖 **LLM usage:** $2.9855 (48 commits)
37
+ - 🤖 **LLM usage:** $3.0029 (50 commits)
38
38
  - 👤 **Human dev:** ~$2047 (20.5h @ $100/h, 30min dedup)
39
39
 
40
40
  Generated on 2026-05-22 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
41
41
 
42
42
  ---
43
43
 
44
- ![PyPI](https://img.shields.io/badge/pypi-wup-blue) ![Version](https://img.shields.io/badge/version-0.2.38-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
44
+ ![PyPI](https://img.shields.io/badge/pypi-wup-blue) ![Version](https://img.shields.io/badge/version-0.2.40-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
45
45
 
46
46
  **WUP (What's Up)** - Intelligent file watcher for regression testing in large projects.
47
47
 
@@ -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.38-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.99-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-20.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.40-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-$3.00-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-20.5h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
8
8
 
9
- - 🤖 **LLM usage:** $2.9855 (48 commits)
9
+ - 🤖 **LLM usage:** $3.0029 (50 commits)
10
10
  - 👤 **Human dev:** ~$2047 (20.5h @ $100/h, 30min dedup)
11
11
 
12
12
  Generated on 2026-05-22 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.38-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.40-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
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "wup"
7
- version = "0.2.38"
7
+ version = "0.2.40"
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.38"
10
+ __version__ = "0.2.40"
11
11
  __author__ = "Tom Sapletta"
12
12
 
13
13
  from .config import load_config, save_config, get_default_config
@@ -152,6 +152,8 @@ def watch(
152
152
  Defaults (no extra flags): ``--mode testql`` and live probes every **60s**
153
153
  (unless ``testql.probe_interval_s`` is set in wup.yaml). Use
154
154
  ``--mode default`` for the legacy HTTP-only watcher without TestQL.
155
+
156
+ If wup.yaml doesn't exist, it will be auto-generated based on project detection.
155
157
  """
156
158
  project_path = Path(project).resolve()
157
159
 
@@ -160,6 +162,14 @@ def watch(
160
162
  raise typer.Exit(1)
161
163
 
162
164
  config_path = Path(config) if config else None
165
+
166
+ # Auto-generate config if it doesn't exist
167
+ if not find_config_file(project_path):
168
+ console.print("[cyan]🔍 No wup.yaml found - auto-detecting project type...[/cyan]")
169
+ _auto_generate_config(project_path, mode)
170
+ console.print("[green]✓ Auto-generated wup.yaml configuration[/green]")
171
+ console.print()
172
+
163
173
  wup_config = _load_watch_config(project_path, config_path, probe_interval, mode)
164
174
  effective_scenarios_dir = scenarios_dir or wup_config.testql.scenario_dir
165
175
 
@@ -191,6 +201,32 @@ def watch(
191
201
  watcher.start_watching()
192
202
 
193
203
 
204
+ def _auto_generate_config(project_path: Path, mode: str):
205
+ """Auto-generate wup.yaml based on project detection."""
206
+ from .cli_scanner import CLIScanner
207
+ from .cli_config_generator import CLIConfigGenerator
208
+ from .config import save_config, get_default_config
209
+
210
+ # Try CLI detection first
211
+ scanner = CLIScanner(str(project_path))
212
+ packages = scanner.scan()
213
+
214
+ if packages:
215
+ console.print("[cyan]📦 Detected CLI package(s)[/cyan]")
216
+ for pkg in packages:
217
+ console.print(f" - {pkg.name}: {len(pkg.commands)} command(s)")
218
+ console.print()
219
+
220
+ # Use CLI config generator
221
+ generator = CLIConfigGenerator(str(project_path))
222
+ generator.generate()
223
+ else:
224
+ # Use default config for web/mixed projects
225
+ console.print("[cyan]🌐 Using default configuration for web/mixed projects[/cyan]")
226
+ config = get_default_config(project_path)
227
+ save_config(config, project_path / "wup.yaml")
228
+
229
+
194
230
  @app.command()
195
231
  def map_deps(
196
232
  project: str = typer.Argument(".", help="Path to the project root directory"),
@@ -78,8 +78,8 @@ class CLIConfigGenerator:
78
78
  description=f"CLI testing for {self.project_root.name}",
79
79
  ),
80
80
  watch=WatchConfig(
81
- paths=["**/*.py"],
82
- exclude_patterns=["*.md", "tests/**", ".venv/**", "venv/**"],
81
+ paths=["**"],
82
+ exclude_patterns=["*.md", "tests/**", ".venv/**", "venv/**", "node_modules/**"],
83
83
  file_types=[".py"],
84
84
  ),
85
85
  test_strategy=TestStrategyConfig(
@@ -175,7 +175,7 @@ def validate_config(raw: dict) -> WupConfig:
175
175
  scenario_dir=testql_raw.get("scenario_dir", "scenarios/tests"),
176
176
  smoke_scenario=testql_raw.get("smoke_scenario", "smoke.testql.toon.yaml"),
177
177
  output_format=testql_raw.get("output_format", "json"),
178
- extra_args=testql_raw.get("extra_args", ["--timeout 10s"]),
178
+ extra_args=testql_raw.get("extra_args", ["--timeout", "10s"]),
179
179
  endpoint_discovery=testql_raw.get("endpoint_discovery", True),
180
180
  probe_interval_s=int(testql_raw.get("probe_interval_s", 0) or 0),
181
181
  health_scenario=testql_raw.get("health_scenario", ""),
@@ -61,7 +61,7 @@ class TestQLConfig:
61
61
  scenario_dir: str = "scenarios/tests"
62
62
  smoke_scenario: str = "smoke.testql.toon.yaml"
63
63
  output_format: str = "json"
64
- extra_args: List[str] = field(default_factory=lambda: ["--timeout 10s"])
64
+ extra_args: List[str] = field(default_factory=lambda: ["--timeout", "10s"])
65
65
  endpoint_discovery: bool = True # Merge health probes from scenarios + service maps
66
66
  probe_interval_s: int = 0 # Periodic live probes for all services (0 = file-change only)
67
67
  health_scenario: str = "" # Fleet TestQL scenario on each periodic probe cycle (live run)
@@ -312,6 +312,19 @@ class TestQLWatcher(WupWatcher):
312
312
  """Score a scenario by relevance to service tokens."""
313
313
  name = scenario.name.lower()
314
314
  score = 0
315
+
316
+ # CLI scenarios require exact service name match
317
+ if name.startswith("cli-"):
318
+ # Extract service name from cli-{service}.testql.toon.yaml
319
+ scenario_service = name.replace("cli-", "").replace(".testql.toon.yaml", "")
320
+ # Score only if service name matches exactly
321
+ if scenario_service in tokens:
322
+ score += 10 # High score for exact match
323
+ else:
324
+ score = -10 # Penalize CLI scenarios that don't match
325
+ return score
326
+
327
+ # Non-CLI scenarios use original scoring
315
328
  if any(token in name for token in tokens):
316
329
  score += 3
317
330
  if "api" in name or "endpoint" in name:
@@ -331,9 +344,13 @@ class TestQLWatcher(WupWatcher):
331
344
  limit = (svc_config.quick_tests.max_endpoints
332
345
  if svc_config and svc_config.quick_tests else self.quick_limit)
333
346
 
347
+ # Filter scenarios by service type
348
+ svc_type = svc_config.type if svc_config else "auto"
349
+ filtered_scenarios = self._filter_scenarios_by_type(all_scenarios, svc_type)
350
+
334
351
  tokens = self._tokenize_service(service)
335
352
  scored = sorted(
336
- ((self._score_scenario(s, tokens), s) for s in all_scenarios),
353
+ ((self._score_scenario(s, tokens), s) for s in filtered_scenarios),
337
354
  key=lambda item: (item[0], item[1].name),
338
355
  reverse=True,
339
356
  )
@@ -346,10 +363,45 @@ class TestQLWatcher(WupWatcher):
346
363
  for base in (self.scenarios_dir, self.project_root):
347
364
  candidate = base / smoke_name
348
365
  if candidate.exists():
349
- return [candidate]
366
+ # Only use smoke scenario if it matches service type
367
+ if self._scenario_matches_type(candidate, svc_type):
368
+ return [candidate]
369
+
370
+ # Fallback: don't return any scenarios for web services if no match found
371
+ # This prevents CLI scenarios from being assigned to web services
372
+ if svc_type == "web":
373
+ return []
350
374
 
351
375
  return []
352
376
 
377
+ def _filter_scenarios_by_type(self, scenarios: List[Path], svc_type: str) -> List[Path]:
378
+ """Filter scenarios by service type (web vs shell)."""
379
+ if svc_type == "auto":
380
+ return scenarios # No filtering for auto type
381
+
382
+ cli_prefix = "cli-"
383
+ if svc_type == "shell":
384
+ # Shell services: only CLI scenarios
385
+ return [s for s in scenarios if s.name.startswith(cli_prefix)]
386
+ elif svc_type == "web":
387
+ # Web services: exclude CLI scenarios
388
+ return [s for s in scenarios if not s.name.startswith(cli_prefix)]
389
+ else:
390
+ return scenarios
391
+
392
+ def _scenario_matches_type(self, scenario: Path, svc_type: str) -> bool:
393
+ """Check if scenario matches service type."""
394
+ if svc_type == "auto":
395
+ return True
396
+
397
+ is_cli = scenario.name.startswith("cli-")
398
+ if svc_type == "shell":
399
+ return is_cli
400
+ elif svc_type == "web":
401
+ return not is_cli
402
+ else:
403
+ return True
404
+
353
405
  def _run_testql(self, args: Sequence[str], timeout: int) -> subprocess.CompletedProcess:
354
406
  cmd = [self.testql_bin, *args]
355
407
  try:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wup
3
- Version: 0.2.38
3
+ Version: 0.2.40
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-Expression: Apache-2.0
@@ -31,17 +31,17 @@ Dynamic: license-file
31
31
 
32
32
  ## AI Cost Tracking
33
33
 
34
- ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.2.38-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
35
- ![AI Cost](https://img.shields.io/badge/AI%20Cost-$2.99-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-20.5h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
34
+ ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.2.40-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
35
+ ![AI Cost](https://img.shields.io/badge/AI%20Cost-$3.00-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-20.5h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
36
36
 
37
- - 🤖 **LLM usage:** $2.9855 (48 commits)
37
+ - 🤖 **LLM usage:** $3.0029 (50 commits)
38
38
  - 👤 **Human dev:** ~$2047 (20.5h @ $100/h, 30min dedup)
39
39
 
40
40
  Generated on 2026-05-22 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
41
41
 
42
42
  ---
43
43
 
44
- ![PyPI](https://img.shields.io/badge/pypi-wup-blue) ![Version](https://img.shields.io/badge/version-0.2.38-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
44
+ ![PyPI](https://img.shields.io/badge/pypi-wup-blue) ![Version](https://img.shields.io/badge/version-0.2.40-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
45
45
 
46
46
  **WUP (What's Up)** - Intelligent file watcher for regression testing in large projects.
47
47
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes