wup 0.2.35__tar.gz → 0.2.38__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.35/wup.egg-info → wup-0.2.38}/PKG-INFO +51 -8
  2. {wup-0.2.35 → wup-0.2.38}/README.md +50 -7
  3. {wup-0.2.35 → wup-0.2.38}/pyproject.toml +1 -1
  4. {wup-0.2.35 → wup-0.2.38}/wup/__init__.py +9 -2
  5. {wup-0.2.35 → wup-0.2.38}/wup/cli.py +81 -0
  6. wup-0.2.38/wup/cli_config_generator.py +223 -0
  7. wup-0.2.38/wup/cli_scanner.py +302 -0
  8. wup-0.2.38/wup/testql_cli_generator.py +215 -0
  9. {wup-0.2.35 → wup-0.2.38}/wup/testql_monitor.py +54 -18
  10. {wup-0.2.35 → wup-0.2.38}/wup/testql_watcher.py +1 -1
  11. {wup-0.2.35 → wup-0.2.38/wup.egg-info}/PKG-INFO +51 -8
  12. {wup-0.2.35 → wup-0.2.38}/wup.egg-info/SOURCES.txt +3 -0
  13. {wup-0.2.35 → wup-0.2.38}/LICENSE +0 -0
  14. {wup-0.2.35 → wup-0.2.38}/setup.cfg +0 -0
  15. {wup-0.2.35 → wup-0.2.38}/tests/test_e2e.py +0 -0
  16. {wup-0.2.35 → wup-0.2.38}/tests/test_monitoring_manifest.py +0 -0
  17. {wup-0.2.35 → wup-0.2.38}/tests/test_testql_monitor.py +0 -0
  18. {wup-0.2.35 → wup-0.2.38}/tests/test_testql_watcher.py +0 -0
  19. {wup-0.2.35 → wup-0.2.38}/tests/test_web_client.py +0 -0
  20. {wup-0.2.35 → wup-0.2.38}/tests/test_wup.py +0 -0
  21. {wup-0.2.35 → wup-0.2.38}/wup/_ast_detector.py +0 -0
  22. {wup-0.2.35 → wup-0.2.38}/wup/_hash_detector.py +0 -0
  23. {wup-0.2.35 → wup-0.2.38}/wup/_yaml_detector.py +0 -0
  24. {wup-0.2.35 → wup-0.2.38}/wup/anomaly_detector.py +0 -0
  25. {wup-0.2.35 → wup-0.2.38}/wup/anomaly_models.py +0 -0
  26. {wup-0.2.35 → wup-0.2.38}/wup/assistant.py +0 -0
  27. {wup-0.2.35 → wup-0.2.38}/wup/config.py +0 -0
  28. {wup-0.2.35 → wup-0.2.38}/wup/core.py +0 -0
  29. {wup-0.2.35 → wup-0.2.38}/wup/dependency_mapper.py +0 -0
  30. {wup-0.2.35 → wup-0.2.38}/wup/models/__init__.py +0 -0
  31. {wup-0.2.35 → wup-0.2.38}/wup/models/config.py +0 -0
  32. {wup-0.2.35 → wup-0.2.38}/wup/monitoring_manifest.py +0 -0
  33. {wup-0.2.35 → wup-0.2.38}/wup/planfile_reporter.py +0 -0
  34. {wup-0.2.35 → wup-0.2.38}/wup/testql_discovery.py +0 -0
  35. {wup-0.2.35 → wup-0.2.38}/wup/visual_diff.py +0 -0
  36. {wup-0.2.35 → wup-0.2.38}/wup/web_client.py +0 -0
  37. {wup-0.2.35 → wup-0.2.38}/wup.egg-info/dependency_links.txt +0 -0
  38. {wup-0.2.35 → wup-0.2.38}/wup.egg-info/entry_points.txt +0 -0
  39. {wup-0.2.35 → wup-0.2.38}/wup.egg-info/requires.txt +0 -0
  40. {wup-0.2.35 → wup-0.2.38}/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.35
3
+ Version: 0.2.38
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.35-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.71-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-19.2h-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.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)
36
36
 
37
- - 🤖 **LLM usage:** $2.7060 (45 commits)
38
- - 👤 **Human dev:** ~$1917 (19.2h @ $100/h, 30min dedup)
37
+ - 🤖 **LLM usage:** $2.9855 (48 commits)
38
+ - 👤 **Human dev:** ~$2047 (20.5h @ $100/h, 30min dedup)
39
39
 
40
- Generated on 2026-05-21 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
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.35-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.38-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
 
@@ -62,6 +62,7 @@ WUP monitors file changes and runs intelligent regression tests using a 3-layer
62
62
  - ⚙️ **Configuration System**: Declarative configuration via `wup.yaml` file
63
63
  - 🎛️ **Per-Service Settings**: Custom test strategies per service
64
64
  - 🧪 **TestQL Integration**: Native support for TestQL scenarios
65
+ - 🔧 **CLI/Shell Automation**: Auto-detection and configuration of CLI tools and shell scripts
65
66
 
66
67
  ## Installation
67
68
 
@@ -143,6 +144,12 @@ wup init
143
144
 
144
145
  # Generate with custom output path
145
146
  wup init --output .wup.yaml
147
+
148
+ # Auto-detect and configure CLI/shell services
149
+ wup init-cli ./my-project
150
+
151
+ # Merge CLI configuration with existing wup.yaml
152
+ wup init-cli ./my-project --merge
146
153
  ```
147
154
 
148
155
  ### Check Status
@@ -155,6 +162,35 @@ wup status
155
162
  wup status --deps my-deps.json
156
163
  ```
157
164
 
165
+ ### CLI/Shell Automation
166
+
167
+ WUP can automatically detect and configure CLI tools and shell scripts for testing:
168
+
169
+ ```bash
170
+ # Auto-detect CLI commands and generate configuration
171
+ wup init-cli ./my-project
172
+
173
+ # Merge with existing wup.yaml
174
+ wup init-cli ./my-project --merge
175
+
176
+ # Custom output paths
177
+ wup init-cli ./my-project --output-config custom.yaml --output-scenarios custom-scenarios
178
+
179
+ # Skip argument inference (faster)
180
+ wup init-cli ./my-project --no-infer-args
181
+ ```
182
+
183
+ The `init-cli` command:
184
+ - Scans `pyproject.toml`, `setup.py`, `setup.cfg` for entry points
185
+ - Detects packages with `__main__.py` modules
186
+ - Generates shell service configuration in `wup.yaml`
187
+ - Creates TestQL scenarios for CLI commands in `testql-scenarios/`
188
+ - Supports merging with existing configurations
189
+
190
+ **Generated files:**
191
+ - `cli-smoke.testql.toon.yaml` - Smoke tests for all CLI commands
192
+ - `cli-{command}.testql.toon.yaml` - Detailed tests for each command
193
+
158
194
  ## Architecture
159
195
 
160
196
  ### 3-Layer Testing Approach
@@ -417,7 +453,10 @@ wup/
417
453
  │ ├── __init__.py # Package exports
418
454
  │ ├── anomaly_detector.py # AnomalyDetector: hash, YAML structure, AST diff
419
455
  │ ├── assistant.py # WupAssistant: interactive configuration wizard
420
- │ ├── cli.py # CLI: watch, map-deps, status, init, testql-endpoints, assistant, version
456
+ │ ├── cli.py # CLI: watch, map-deps, status, init, testql-endpoints, assistant, version, init-cli
457
+ │ ├── cli_scanner.py # CLIScanner: detects CLI commands from pyproject.toml, setup.py, etc.
458
+ │ ├── cli_config_generator.py # CLIConfigGenerator: generates wup.yaml for shell services
459
+ │ ├── testql_cli_generator.py # TestQLCliGenerator: generates TestQL scenarios for CLI commands
421
460
  │ ├── config.py # Config loading/saving + .wup.env support
422
461
  │ ├── core.py # WupWatcher: detection, inference, scheduling
423
462
  │ ├── dependency_mapper.py # DependencyMapper: codebase → deps.json
@@ -519,6 +558,7 @@ Comprehensive documentation is available in the `docs/` directory:
519
558
  - `wup assistant` - Interactive configuration wizard
520
559
  - Auto-detects framework and services
521
560
  - Intelligent suggestions and validation
561
+ - `wup init-cli` - Automated CLI/shell project setup
522
562
 
523
563
  - **[Anomaly Detection](docs/ANOMALY_DETECTION.md)** - Fast alternatives to Playwright
524
564
  - Hash-based change detection (~1ms per file)
@@ -533,6 +573,9 @@ Comprehensive documentation is available in the `docs/` directory:
533
573
  - Browser Notifications API integration
534
574
 
535
575
  - **[TestQL Integration](docs/TESTQL_INTEGRATION.md)** - TestQL scenario support
576
+ - `wup init-cli` - Automated CLI command detection and TestQL scenario generation
577
+ - Shell service configuration for CLI tools
578
+ - TestQL scenarios for CLI commands
536
579
 
537
580
  ## License
538
581
 
@@ -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.35-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.71-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-19.2h-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.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)
8
8
 
9
- - 🤖 **LLM usage:** $2.7060 (45 commits)
10
- - 👤 **Human dev:** ~$1917 (19.2h @ $100/h, 30min dedup)
9
+ - 🤖 **LLM usage:** $2.9855 (48 commits)
10
+ - 👤 **Human dev:** ~$2047 (20.5h @ $100/h, 30min dedup)
11
11
 
12
- Generated on 2026-05-21 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
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.35-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.38-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
 
@@ -34,6 +34,7 @@ WUP monitors file changes and runs intelligent regression tests using a 3-layer
34
34
  - ⚙️ **Configuration System**: Declarative configuration via `wup.yaml` file
35
35
  - 🎛️ **Per-Service Settings**: Custom test strategies per service
36
36
  - 🧪 **TestQL Integration**: Native support for TestQL scenarios
37
+ - 🔧 **CLI/Shell Automation**: Auto-detection and configuration of CLI tools and shell scripts
37
38
 
38
39
  ## Installation
39
40
 
@@ -115,6 +116,12 @@ wup init
115
116
 
116
117
  # Generate with custom output path
117
118
  wup init --output .wup.yaml
119
+
120
+ # Auto-detect and configure CLI/shell services
121
+ wup init-cli ./my-project
122
+
123
+ # Merge CLI configuration with existing wup.yaml
124
+ wup init-cli ./my-project --merge
118
125
  ```
119
126
 
120
127
  ### Check Status
@@ -127,6 +134,35 @@ wup status
127
134
  wup status --deps my-deps.json
128
135
  ```
129
136
 
137
+ ### CLI/Shell Automation
138
+
139
+ WUP can automatically detect and configure CLI tools and shell scripts for testing:
140
+
141
+ ```bash
142
+ # Auto-detect CLI commands and generate configuration
143
+ wup init-cli ./my-project
144
+
145
+ # Merge with existing wup.yaml
146
+ wup init-cli ./my-project --merge
147
+
148
+ # Custom output paths
149
+ wup init-cli ./my-project --output-config custom.yaml --output-scenarios custom-scenarios
150
+
151
+ # Skip argument inference (faster)
152
+ wup init-cli ./my-project --no-infer-args
153
+ ```
154
+
155
+ The `init-cli` command:
156
+ - Scans `pyproject.toml`, `setup.py`, `setup.cfg` for entry points
157
+ - Detects packages with `__main__.py` modules
158
+ - Generates shell service configuration in `wup.yaml`
159
+ - Creates TestQL scenarios for CLI commands in `testql-scenarios/`
160
+ - Supports merging with existing configurations
161
+
162
+ **Generated files:**
163
+ - `cli-smoke.testql.toon.yaml` - Smoke tests for all CLI commands
164
+ - `cli-{command}.testql.toon.yaml` - Detailed tests for each command
165
+
130
166
  ## Architecture
131
167
 
132
168
  ### 3-Layer Testing Approach
@@ -389,7 +425,10 @@ wup/
389
425
  │ ├── __init__.py # Package exports
390
426
  │ ├── anomaly_detector.py # AnomalyDetector: hash, YAML structure, AST diff
391
427
  │ ├── assistant.py # WupAssistant: interactive configuration wizard
392
- │ ├── cli.py # CLI: watch, map-deps, status, init, testql-endpoints, assistant, version
428
+ │ ├── cli.py # CLI: watch, map-deps, status, init, testql-endpoints, assistant, version, init-cli
429
+ │ ├── cli_scanner.py # CLIScanner: detects CLI commands from pyproject.toml, setup.py, etc.
430
+ │ ├── cli_config_generator.py # CLIConfigGenerator: generates wup.yaml for shell services
431
+ │ ├── testql_cli_generator.py # TestQLCliGenerator: generates TestQL scenarios for CLI commands
393
432
  │ ├── config.py # Config loading/saving + .wup.env support
394
433
  │ ├── core.py # WupWatcher: detection, inference, scheduling
395
434
  │ ├── dependency_mapper.py # DependencyMapper: codebase → deps.json
@@ -491,6 +530,7 @@ Comprehensive documentation is available in the `docs/` directory:
491
530
  - `wup assistant` - Interactive configuration wizard
492
531
  - Auto-detects framework and services
493
532
  - Intelligent suggestions and validation
533
+ - `wup init-cli` - Automated CLI/shell project setup
494
534
 
495
535
  - **[Anomaly Detection](docs/ANOMALY_DETECTION.md)** - Fast alternatives to Playwright
496
536
  - Hash-based change detection (~1ms per file)
@@ -505,6 +545,9 @@ Comprehensive documentation is available in the `docs/` directory:
505
545
  - Browser Notifications API integration
506
546
 
507
547
  - **[TestQL Integration](docs/TESTQL_INTEGRATION.md)** - TestQL scenario support
548
+ - `wup init-cli` - Automated CLI command detection and TestQL scenario generation
549
+ - Shell service configuration for CLI tools
550
+ - TestQL scenarios for CLI commands
508
551
 
509
552
  ## License
510
553
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "wup"
7
- version = "0.2.35"
7
+ version = "0.2.38"
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.35"
10
+ __version__ = "0.2.38"
11
11
  __author__ = "Tom Sapletta"
12
12
 
13
13
  from .config import load_config, save_config, get_default_config
@@ -21,7 +21,6 @@ from .models.config import (
21
21
  TestQLConfig,
22
22
  NotifyConfig,
23
23
  )
24
- from .testql_watcher import TestQLWatcher
25
24
 
26
25
  __all__ = [
27
26
  "WupWatcher",
@@ -37,3 +36,11 @@ __all__ = [
37
36
  "TestQLConfig",
38
37
  "NotifyConfig",
39
38
  ]
39
+
40
+
41
+ def __getattr__(name: str):
42
+ """Lazy import for TestQLWatcher to avoid circular dependency."""
43
+ if name == "TestQLWatcher":
44
+ from .testql_watcher import TestQLWatcher
45
+ return TestQLWatcher
46
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
@@ -678,5 +678,86 @@ def version():
678
678
  console.print(f"[bold cyan]WUP[/bold cyan] version [green]{__version__}[/green]")
679
679
 
680
680
 
681
+ @app.command("init-cli")
682
+ def init_cli(
683
+ project: str = typer.Argument(".", help="Path to the project root directory"),
684
+ output_config: Optional[str] = typer.Option(None, "--output-config", "-c", help="Path for wup.yaml output"),
685
+ output_scenarios: Optional[str] = typer.Option(None, "--output-scenarios", "-s", help="Path for testql-scenarios directory"),
686
+ merge: bool = typer.Option(False, "--merge", "-m", help="Merge with existing wup.yaml"),
687
+ infer_args: bool = typer.Option(True, "--infer-args/--no-infer-args", help="Infer command arguments by inspection"),
688
+ ):
689
+ """
690
+ Automatically generate wup.yaml configuration and TestQL scenarios for CLI/shell services.
691
+
692
+ Scans the project for CLI commands (entry points, setup.py, pyproject.toml) and generates:
693
+ - wup.yaml with shell service configuration
694
+ - TestQL scenarios in testql-scenarios/ directory
695
+
696
+ Example:
697
+ wup init-cli ./my-project
698
+ wup init-cli ./my-project --merge
699
+ """
700
+ from pathlib import Path
701
+ from .cli_scanner import CLIScanner
702
+ from .cli_config_generator import CLIConfigGenerator
703
+ from .testql_cli_generator import TestQLCLIGenerator
704
+
705
+ project_path = Path(project).resolve()
706
+
707
+ if not project_path.exists():
708
+ console.print(f"[red]Error: Project path '{project}' does not exist[/red]")
709
+ raise typer.Exit(1)
710
+
711
+ console.print(f"[cyan]🔍 Scanning project for CLI commands...[/cyan]")
712
+ console.print(f"[dim]Project: {project_path}[/dim]\n")
713
+
714
+ try:
715
+ # Scan for CLI commands
716
+ scanner = CLIScanner(str(project_path))
717
+ packages = scanner.scan()
718
+
719
+ if not packages:
720
+ console.print("[yellow]⚠ No CLI packages found in project[/yellow]")
721
+ console.print("[dim]Looking for: setup.py, pyproject.toml, or packages with __main__.py[/dim]")
722
+ raise typer.Exit(1)
723
+
724
+ console.print(f"[green]✓ Found {len(packages)} package(s)[/green]")
725
+ for pkg in packages:
726
+ console.print(f" [cyan]{pkg.name}[/cyan]: {len(pkg.commands)} command(s)")
727
+ for cmd in pkg.commands:
728
+ console.print(f" - {cmd.name} -> {cmd.entry_point}")
729
+ console.print()
730
+
731
+ # Generate wup.yaml
732
+ config_generator = CLIConfigGenerator(str(project_path))
733
+ config_output = Path(output_config) if output_config else None
734
+ config = config_generator.generate(output_path=config_output, merge_existing=merge)
735
+ config_generator.print_summary(config)
736
+
737
+ # Generate TestQL scenarios
738
+ console.print()
739
+ console.print(f"[cyan]🧪 Generating TestQL scenarios...[/cyan]")
740
+ scenarios_output = Path(output_scenarios) if output_scenarios else None
741
+ testql_generator = TestQLCLIGenerator(str(project_path))
742
+ generated_files = testql_generator.generate(
743
+ output_dir=scenarios_output,
744
+ infer_args=infer_args,
745
+ )
746
+ testql_generator.print_summary(generated_files)
747
+
748
+ console.print()
749
+ console.print("[bold green]✅ CLI testing setup complete![/bold green]")
750
+ console.print()
751
+ console.print("[dim]Next steps:[/dim]")
752
+ console.print(" 1. Review generated wup.yaml")
753
+ console.print(" 2. Review testql-scenarios/*.testql.toon.yaml")
754
+ console.print(" 3. Run: wup watch . --mode testql")
755
+ console.print(" 4. Or run individual scenario: testql run testql-scenarios/cli-smoke.testql.toon.yaml")
756
+
757
+ except Exception as e:
758
+ console.print(f"[red]Error: {e}[/red]")
759
+ raise typer.Exit(1)
760
+
761
+
681
762
  if __name__ == "__main__":
682
763
  app()
@@ -0,0 +1,223 @@
1
+ """Generator for wup.yaml configuration for CLI/shell services."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+ from typing import Dict, List, Optional
7
+
8
+ import yaml
9
+
10
+ from .cli_scanner import CLIScanner, CLIPackage, CLICommand
11
+ from .models.config import (
12
+ ProjectConfig,
13
+ ServiceConfig,
14
+ ServiceTestConfig,
15
+ TestQLConfig,
16
+ TestStrategyConfig,
17
+ WatchConfig,
18
+ WupConfig,
19
+ )
20
+
21
+
22
+ class CLIConfigGenerator:
23
+ """Generate wup.yaml configuration for CLI/shell services."""
24
+
25
+ def __init__(self, project_root: str):
26
+ self.project_root = Path(project_root).resolve()
27
+ self.scanner = CLIScanner(project_root)
28
+
29
+ def generate(
30
+ self,
31
+ output_path: Optional[Path] = None,
32
+ merge_existing: bool = False,
33
+ ) -> WupConfig:
34
+ """Generate wup.yaml configuration for CLI services.
35
+
36
+ Args:
37
+ output_path: Path to save the configuration (default: wup.yaml)
38
+ merge_existing: If True, merge with existing wup.yaml
39
+
40
+ Returns:
41
+ Generated WupConfig object
42
+ """
43
+ # Scan for CLI commands
44
+ packages = self.scanner.scan()
45
+
46
+ if not packages:
47
+ raise ValueError("No CLI packages found in project")
48
+
49
+ # Load existing config if merging
50
+ existing_config = None
51
+ if merge_existing:
52
+ from .config import load_config
53
+ existing_config = load_config(self.project_root)
54
+
55
+ # Generate configuration
56
+ config = self._generate_config(packages, existing_config)
57
+
58
+ # Save configuration
59
+ if output_path is None:
60
+ output_path = self.project_root / "wup.yaml"
61
+
62
+ self._save_config(config, output_path)
63
+
64
+ return config
65
+
66
+ def _generate_config(
67
+ self,
68
+ packages: List[CLIPackage],
69
+ existing_config: Optional[WupConfig] = None,
70
+ ) -> WupConfig:
71
+ """Generate WupConfig from scanned packages."""
72
+ if existing_config:
73
+ config = existing_config
74
+ else:
75
+ config = WupConfig(
76
+ project=ProjectConfig(
77
+ name=self.project_root.name,
78
+ description=f"CLI testing for {self.project_root.name}",
79
+ ),
80
+ watch=WatchConfig(
81
+ paths=["**/*.py"],
82
+ exclude_patterns=["*.md", "tests/**", ".venv/**", "venv/**"],
83
+ file_types=[".py"],
84
+ ),
85
+ test_strategy=TestStrategyConfig(
86
+ quick={"debounce_s": 2, "max_queue": 5, "timeout_s": 30},
87
+ detail={"debounce_s": 10, "max_queue": 1, "timeout_s": 60},
88
+ ),
89
+ testql=TestQLConfig(
90
+ scenario_dir="testql-scenarios",
91
+ smoke_scenario="cli-smoke.testql.toon.yaml",
92
+ output_format="json",
93
+ probe_interval_s=60,
94
+ ),
95
+ )
96
+
97
+ # Add shell services for each package
98
+ for package in packages:
99
+ service = self._create_shell_service(package)
100
+
101
+ # Check if service already exists
102
+ existing_service = next(
103
+ (s for s in config.services if s.name == service.name),
104
+ None,
105
+ )
106
+
107
+ if existing_service:
108
+ # Update existing service
109
+ existing_service.type = service.type
110
+ existing_service.quick_tests = service.quick_tests
111
+ existing_service.detail_tests = service.detail_tests
112
+ else:
113
+ # Add new service
114
+ config.services.append(service)
115
+
116
+ return config
117
+
118
+ def _create_shell_service(self, package: CLIPackage) -> ServiceConfig:
119
+ """Create a shell service configuration from a CLI package."""
120
+ # Calculate max_endpoints based on number of commands
121
+ command_count = len(package.commands)
122
+ quick_max = min(command_count, 5)
123
+ detail_max = min(command_count * 2, 20)
124
+
125
+ service = ServiceConfig(
126
+ name=f"{package.name}-shell",
127
+ type="shell",
128
+ paths=[], # Auto-detect
129
+ root=str(self.project_root),
130
+ quick_tests=ServiceTestConfig(
131
+ scope="all",
132
+ max_endpoints=quick_max,
133
+ ),
134
+ detail_tests=ServiceTestConfig(
135
+ scope="all",
136
+ max_endpoints=detail_max,
137
+ ),
138
+ )
139
+
140
+ return service
141
+
142
+ def _save_config(self, config: WupConfig, output_path: Path) -> None:
143
+ """Save configuration to YAML file."""
144
+ output_path.parent.mkdir(parents=True, exist_ok=True)
145
+
146
+ # Convert to dict
147
+ config_dict = {
148
+ "project": {
149
+ "name": config.project.name,
150
+ "description": config.project.description,
151
+ },
152
+ "watch": {
153
+ "paths": config.watch.paths,
154
+ "exclude_patterns": config.watch.exclude_patterns,
155
+ "file_types": config.watch.file_types,
156
+ },
157
+ "services": [
158
+ {
159
+ "name": svc.name,
160
+ "type": svc.type,
161
+ "paths": svc.paths,
162
+ "root": svc.root,
163
+ "quick_tests": {
164
+ "scope": svc.quick_tests.scope,
165
+ "max_endpoints": svc.quick_tests.max_endpoints,
166
+ },
167
+ "detail_tests": {
168
+ "scope": svc.detail_tests.scope,
169
+ "max_endpoints": svc.detail_tests.max_endpoints,
170
+ },
171
+ }
172
+ for svc in config.services
173
+ ],
174
+ "test_strategy": {
175
+ "quick": config.test_strategy.quick,
176
+ "detail": config.test_strategy.detail,
177
+ },
178
+ "testql": {
179
+ "scenario_dir": config.testql.scenario_dir,
180
+ "smoke_scenario": config.testql.smoke_scenario,
181
+ "output_format": config.testql.output_format,
182
+ "probe_interval_s": config.testql.probe_interval_s,
183
+ },
184
+ }
185
+
186
+ # Add header
187
+ header = f"""# WUP Configuration for CLI Testing
188
+ # Generated automatically by wup init-cli
189
+ # Project: {config.project.name}
190
+
191
+ """
192
+
193
+ with open(output_path, "w", encoding="utf-8") as f:
194
+ f.write(header)
195
+ yaml.dump(config_dict, f, default_flow_style=False, sort_keys=False)
196
+
197
+ def print_summary(self, config: WupConfig) -> None:
198
+ """Print summary of generated configuration."""
199
+ from rich.console import Console
200
+ from rich.table import Table
201
+
202
+ console = Console()
203
+
204
+ console.print("\n[bold green]✓ Generated wup.yaml configuration[/bold green]\n")
205
+
206
+ table = Table(title="Shell Services")
207
+ table.add_column("Service", style="cyan")
208
+ table.add_column("Type", style="green")
209
+ table.add_column("Quick Tests", style="yellow")
210
+ table.add_column("Detail Tests", style="yellow")
211
+
212
+ for svc in config.services:
213
+ if svc.type == "shell":
214
+ table.add_row(
215
+ svc.name,
216
+ svc.type,
217
+ str(svc.quick_tests.max_endpoints),
218
+ str(svc.detail_tests.max_endpoints),
219
+ )
220
+
221
+ console.print(table)
222
+ console.print(f"\nTestQL scenarios directory: {config.testql.scenario_dir}")
223
+ console.print(f"Smoke scenario: {config.testql.smoke_scenario}")