adversarial-workflow 0.6.0__tar.gz → 0.6.1__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.
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/PKG-INFO +34 -4
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/README.md +33 -3
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow/__init__.py +1 -1
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow/cli.py +29 -10
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow.egg-info/PKG-INFO +34 -4
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow.egg-info/SOURCES.txt +1 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/pyproject.toml +1 -1
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/tests/test_cli.py +1 -1
- adversarial_workflow-0.6.1/tests/test_env_loading.py +214 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/LICENSE +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow/__main__.py +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow/evaluators/__init__.py +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow/evaluators/builtins.py +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow/evaluators/config.py +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow/evaluators/discovery.py +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow/evaluators/runner.py +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow/templates/.aider.conf.yml.template +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow/templates/.env.example.template +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow/templates/README.template +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow/templates/agent-context/AGENT-SYSTEM-GUIDE.md +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow/templates/agent-context/README.md.template +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow/templates/agent-context/agent-handoffs-minimal.json.template +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow/templates/agent-context/agent-handoffs.json.template +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow/templates/agent-context/current-state.json.template +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow/templates/config.yml.template +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow/templates/evaluate_plan.sh.template +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow/templates/example-task.md.template +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow/templates/proofread_content.sh.template +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow/templates/review_implementation.sh.template +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow/templates/validate_tests.sh.template +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow/utils/__init__.py +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow/utils/colors.py +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow/utils/config.py +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow/utils/file_splitter.py +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow/utils/validation.py +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow.egg-info/dependency_links.txt +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow.egg-info/entry_points.txt +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow.egg-info/requires.txt +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow.egg-info/top_level.txt +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/setup.cfg +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/setup.py +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/tests/test_cli_dynamic_commands.py +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/tests/test_config.py +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/tests/test_evaluate.py +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/tests/test_evaluator_config.py +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/tests/test_evaluator_discovery.py +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/tests/test_evaluator_runner.py +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/tests/test_file_splitter.py +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/tests/test_list_evaluators.py +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/tests/test_python_version.py +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/tests/test_split_command.py +0 -0
- {adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/tests/test_utils_validation.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: adversarial-workflow
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.1
|
|
4
4
|
Summary: Multi-stage AI code review system preventing phantom work - Author/Evaluator pattern
|
|
5
5
|
Author: Fredrik Matheson
|
|
6
6
|
License: MIT
|
|
@@ -35,6 +35,10 @@ Dynamic: license-file
|
|
|
35
35
|
|
|
36
36
|
# Adversarial Workflow
|
|
37
37
|
|
|
38
|
+
[](https://pypi.org/project/adversarial-workflow/)
|
|
39
|
+
[](https://www.python.org/downloads/)
|
|
40
|
+
[](https://opensource.org/licenses/MIT)
|
|
41
|
+
|
|
38
42
|
**A multi-stage AI code review system that makes your code better**
|
|
39
43
|
|
|
40
44
|
Evaluate proposals, sort out ideas, and prevent "phantom work" (AI claiming to implement but not delivering) through adversarial verification using independent review stages. A battle-tested workflow from the [thematic-cuts](https://github.com/movito/thematic-cuts) project that achieved 96.9% test pass rate improvement.
|
|
@@ -51,6 +55,31 @@ Evaluate proposals, sort out ideas, and prevent "phantom work" (AI claiming to i
|
|
|
51
55
|
- 🎯 **Tool-agnostic**: Use with Claude Code, Cursor, Aider, manual coding, or any workflow
|
|
52
56
|
- ✨ **Interactive onboarding**: Guided setup wizard gets you started in <5 minutes
|
|
53
57
|
|
|
58
|
+
## What's New in v0.6.0
|
|
59
|
+
|
|
60
|
+
🔌 **Plugin Architecture** - Define custom evaluators without modifying the package:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# Create a custom evaluator
|
|
64
|
+
mkdir -p .adversarial/evaluators
|
|
65
|
+
cat > .adversarial/evaluators/athena.yml << 'EOF'
|
|
66
|
+
name: athena
|
|
67
|
+
description: Knowledge evaluation using Gemini 2.5 Pro
|
|
68
|
+
model: gemini-2.5-pro
|
|
69
|
+
api_key_env: GEMINI_API_KEY
|
|
70
|
+
prompt: |
|
|
71
|
+
You are Athena, a knowledge evaluation specialist...
|
|
72
|
+
EOF
|
|
73
|
+
|
|
74
|
+
# Use it immediately
|
|
75
|
+
adversarial athena docs/research-plan.md
|
|
76
|
+
|
|
77
|
+
# List all available evaluators
|
|
78
|
+
adversarial list-evaluators
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
See [Custom Evaluators](#custom-evaluators) for full documentation, or check the [CHANGELOG](CHANGELOG.md) for complete release history.
|
|
82
|
+
|
|
54
83
|
## Prerequisites
|
|
55
84
|
|
|
56
85
|
Before installing, ensure you have:
|
|
@@ -856,12 +885,13 @@ From the [thematic-cuts](https://github.com/movito/thematic-cuts) project:
|
|
|
856
885
|
|
|
857
886
|
## Documentation
|
|
858
887
|
|
|
859
|
-
- **
|
|
888
|
+
- **[Custom Evaluators Guide](docs/CUSTOM_EVALUATORS.md)**: Create project-specific evaluators
|
|
889
|
+
- **[Integration Guide](docs/INTEGRATION-GUIDE.md)**: Detailed integration strategies
|
|
890
|
+
- **[CHANGELOG](CHANGELOG.md)**: Release history and version notes
|
|
891
|
+
- **Interaction Patterns**: How Author-Evaluator collaboration works
|
|
860
892
|
- **Token Optimization**: Detailed Aider configuration guide
|
|
861
893
|
- **Workflow Phases**: Step-by-step guide for each phase
|
|
862
894
|
- **Troubleshooting**: Common issues and solutions
|
|
863
|
-
- **Examples**: Real integration scenarios
|
|
864
|
-
- **Terminology**: Official standards for Author/Reviewer concepts
|
|
865
895
|
|
|
866
896
|
See `docs/` directory for comprehensive guides.
|
|
867
897
|
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Adversarial Workflow
|
|
2
2
|
|
|
3
|
+
[](https://pypi.org/project/adversarial-workflow/)
|
|
4
|
+
[](https://www.python.org/downloads/)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
3
7
|
**A multi-stage AI code review system that makes your code better**
|
|
4
8
|
|
|
5
9
|
Evaluate proposals, sort out ideas, and prevent "phantom work" (AI claiming to implement but not delivering) through adversarial verification using independent review stages. A battle-tested workflow from the [thematic-cuts](https://github.com/movito/thematic-cuts) project that achieved 96.9% test pass rate improvement.
|
|
@@ -16,6 +20,31 @@ Evaluate proposals, sort out ideas, and prevent "phantom work" (AI claiming to i
|
|
|
16
20
|
- 🎯 **Tool-agnostic**: Use with Claude Code, Cursor, Aider, manual coding, or any workflow
|
|
17
21
|
- ✨ **Interactive onboarding**: Guided setup wizard gets you started in <5 minutes
|
|
18
22
|
|
|
23
|
+
## What's New in v0.6.0
|
|
24
|
+
|
|
25
|
+
🔌 **Plugin Architecture** - Define custom evaluators without modifying the package:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# Create a custom evaluator
|
|
29
|
+
mkdir -p .adversarial/evaluators
|
|
30
|
+
cat > .adversarial/evaluators/athena.yml << 'EOF'
|
|
31
|
+
name: athena
|
|
32
|
+
description: Knowledge evaluation using Gemini 2.5 Pro
|
|
33
|
+
model: gemini-2.5-pro
|
|
34
|
+
api_key_env: GEMINI_API_KEY
|
|
35
|
+
prompt: |
|
|
36
|
+
You are Athena, a knowledge evaluation specialist...
|
|
37
|
+
EOF
|
|
38
|
+
|
|
39
|
+
# Use it immediately
|
|
40
|
+
adversarial athena docs/research-plan.md
|
|
41
|
+
|
|
42
|
+
# List all available evaluators
|
|
43
|
+
adversarial list-evaluators
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
See [Custom Evaluators](#custom-evaluators) for full documentation, or check the [CHANGELOG](CHANGELOG.md) for complete release history.
|
|
47
|
+
|
|
19
48
|
## Prerequisites
|
|
20
49
|
|
|
21
50
|
Before installing, ensure you have:
|
|
@@ -821,12 +850,13 @@ From the [thematic-cuts](https://github.com/movito/thematic-cuts) project:
|
|
|
821
850
|
|
|
822
851
|
## Documentation
|
|
823
852
|
|
|
824
|
-
- **
|
|
853
|
+
- **[Custom Evaluators Guide](docs/CUSTOM_EVALUATORS.md)**: Create project-specific evaluators
|
|
854
|
+
- **[Integration Guide](docs/INTEGRATION-GUIDE.md)**: Detailed integration strategies
|
|
855
|
+
- **[CHANGELOG](CHANGELOG.md)**: Release history and version notes
|
|
856
|
+
- **Interaction Patterns**: How Author-Evaluator collaboration works
|
|
825
857
|
- **Token Optimization**: Detailed Aider configuration guide
|
|
826
858
|
- **Workflow Phases**: Step-by-step guide for each phase
|
|
827
859
|
- **Troubleshooting**: Common issues and solutions
|
|
828
|
-
- **Examples**: Real integration scenarios
|
|
829
|
-
- **Terminology**: Official standards for Author/Reviewer concepts
|
|
830
860
|
|
|
831
861
|
See `docs/` directory for comprehensive guides.
|
|
832
862
|
|
|
@@ -27,9 +27,9 @@ from pathlib import Path
|
|
|
27
27
|
from typing import Dict, List, Optional, Tuple
|
|
28
28
|
|
|
29
29
|
import yaml
|
|
30
|
-
from dotenv import load_dotenv
|
|
30
|
+
from dotenv import load_dotenv, dotenv_values
|
|
31
31
|
|
|
32
|
-
__version__ = "0.6.
|
|
32
|
+
__version__ = "0.6.1"
|
|
33
33
|
|
|
34
34
|
# ANSI color codes for better output
|
|
35
35
|
RESET = "\033[0m"
|
|
@@ -800,26 +800,37 @@ def check() -> int:
|
|
|
800
800
|
issues: List[Dict] = []
|
|
801
801
|
good_checks: List[str] = []
|
|
802
802
|
|
|
803
|
-
# Check for .env file
|
|
803
|
+
# Check for .env file (note: already loaded by main() at startup)
|
|
804
804
|
env_file = Path(".env")
|
|
805
805
|
env_loaded = False
|
|
806
|
-
env_keys_before = set(os.environ.keys())
|
|
807
806
|
|
|
808
807
|
if env_file.exists():
|
|
809
808
|
try:
|
|
809
|
+
# Load .env into environment (idempotent - safe to call again after main())
|
|
810
810
|
load_dotenv(env_file)
|
|
811
|
-
|
|
812
|
-
|
|
811
|
+
# Use dotenv_values() to count variables directly from file
|
|
812
|
+
# This gives accurate count regardless of what was already in environment
|
|
813
|
+
env_vars = dotenv_values(env_file)
|
|
813
814
|
env_loaded = True
|
|
814
815
|
good_checks.append(
|
|
815
|
-
f".env file found
|
|
816
|
+
f".env file found ({len(env_vars)} variables configured)"
|
|
816
817
|
)
|
|
817
|
-
except
|
|
818
|
+
except (FileNotFoundError, PermissionError) as e:
|
|
819
|
+
# File access errors
|
|
818
820
|
issues.append(
|
|
819
821
|
{
|
|
820
822
|
"severity": "WARNING",
|
|
821
|
-
"message": f".env file found but could not be
|
|
822
|
-
"fix": "Check .env file
|
|
823
|
+
"message": f".env file found but could not be read: {e}",
|
|
824
|
+
"fix": "Check .env file permissions",
|
|
825
|
+
}
|
|
826
|
+
)
|
|
827
|
+
except (OSError, ValueError) as e:
|
|
828
|
+
# Covers UnicodeDecodeError (ValueError subclass) and other OS errors
|
|
829
|
+
issues.append(
|
|
830
|
+
{
|
|
831
|
+
"severity": "WARNING",
|
|
832
|
+
"message": f".env file found but could not be parsed: {e}",
|
|
833
|
+
"fix": "Check .env file encoding (should be UTF-8)",
|
|
823
834
|
}
|
|
824
835
|
)
|
|
825
836
|
else:
|
|
@@ -2868,6 +2879,14 @@ def list_evaluators() -> int:
|
|
|
2868
2879
|
def main():
|
|
2869
2880
|
"""Main CLI entry point."""
|
|
2870
2881
|
import logging
|
|
2882
|
+
import sys
|
|
2883
|
+
|
|
2884
|
+
# Load .env file before any commands run
|
|
2885
|
+
# Wrapped in try/except so CLI remains usable even with malformed .env
|
|
2886
|
+
try:
|
|
2887
|
+
load_dotenv()
|
|
2888
|
+
except Exception as e:
|
|
2889
|
+
print(f"Warning: Could not load .env file: {e}", file=sys.stderr)
|
|
2871
2890
|
|
|
2872
2891
|
from adversarial_workflow.evaluators import (
|
|
2873
2892
|
get_all_evaluators,
|
{adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: adversarial-workflow
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.1
|
|
4
4
|
Summary: Multi-stage AI code review system preventing phantom work - Author/Evaluator pattern
|
|
5
5
|
Author: Fredrik Matheson
|
|
6
6
|
License: MIT
|
|
@@ -35,6 +35,10 @@ Dynamic: license-file
|
|
|
35
35
|
|
|
36
36
|
# Adversarial Workflow
|
|
37
37
|
|
|
38
|
+
[](https://pypi.org/project/adversarial-workflow/)
|
|
39
|
+
[](https://www.python.org/downloads/)
|
|
40
|
+
[](https://opensource.org/licenses/MIT)
|
|
41
|
+
|
|
38
42
|
**A multi-stage AI code review system that makes your code better**
|
|
39
43
|
|
|
40
44
|
Evaluate proposals, sort out ideas, and prevent "phantom work" (AI claiming to implement but not delivering) through adversarial verification using independent review stages. A battle-tested workflow from the [thematic-cuts](https://github.com/movito/thematic-cuts) project that achieved 96.9% test pass rate improvement.
|
|
@@ -51,6 +55,31 @@ Evaluate proposals, sort out ideas, and prevent "phantom work" (AI claiming to i
|
|
|
51
55
|
- 🎯 **Tool-agnostic**: Use with Claude Code, Cursor, Aider, manual coding, or any workflow
|
|
52
56
|
- ✨ **Interactive onboarding**: Guided setup wizard gets you started in <5 minutes
|
|
53
57
|
|
|
58
|
+
## What's New in v0.6.0
|
|
59
|
+
|
|
60
|
+
🔌 **Plugin Architecture** - Define custom evaluators without modifying the package:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# Create a custom evaluator
|
|
64
|
+
mkdir -p .adversarial/evaluators
|
|
65
|
+
cat > .adversarial/evaluators/athena.yml << 'EOF'
|
|
66
|
+
name: athena
|
|
67
|
+
description: Knowledge evaluation using Gemini 2.5 Pro
|
|
68
|
+
model: gemini-2.5-pro
|
|
69
|
+
api_key_env: GEMINI_API_KEY
|
|
70
|
+
prompt: |
|
|
71
|
+
You are Athena, a knowledge evaluation specialist...
|
|
72
|
+
EOF
|
|
73
|
+
|
|
74
|
+
# Use it immediately
|
|
75
|
+
adversarial athena docs/research-plan.md
|
|
76
|
+
|
|
77
|
+
# List all available evaluators
|
|
78
|
+
adversarial list-evaluators
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
See [Custom Evaluators](#custom-evaluators) for full documentation, or check the [CHANGELOG](CHANGELOG.md) for complete release history.
|
|
82
|
+
|
|
54
83
|
## Prerequisites
|
|
55
84
|
|
|
56
85
|
Before installing, ensure you have:
|
|
@@ -856,12 +885,13 @@ From the [thematic-cuts](https://github.com/movito/thematic-cuts) project:
|
|
|
856
885
|
|
|
857
886
|
## Documentation
|
|
858
887
|
|
|
859
|
-
- **
|
|
888
|
+
- **[Custom Evaluators Guide](docs/CUSTOM_EVALUATORS.md)**: Create project-specific evaluators
|
|
889
|
+
- **[Integration Guide](docs/INTEGRATION-GUIDE.md)**: Detailed integration strategies
|
|
890
|
+
- **[CHANGELOG](CHANGELOG.md)**: Release history and version notes
|
|
891
|
+
- **Interaction Patterns**: How Author-Evaluator collaboration works
|
|
860
892
|
- **Token Optimization**: Detailed Aider configuration guide
|
|
861
893
|
- **Workflow Phases**: Step-by-step guide for each phase
|
|
862
894
|
- **Troubleshooting**: Common issues and solutions
|
|
863
|
-
- **Examples**: Real integration scenarios
|
|
864
|
-
- **Terminology**: Official standards for Author/Reviewer concepts
|
|
865
895
|
|
|
866
896
|
See `docs/` directory for comprehensive guides.
|
|
867
897
|
|
|
@@ -26,7 +26,7 @@ class TestCLISmoke:
|
|
|
26
26
|
text=True,
|
|
27
27
|
)
|
|
28
28
|
assert result.returncode == 0
|
|
29
|
-
assert "0.6.
|
|
29
|
+
assert "0.6.1" in result.stdout or "0.6.1" in result.stderr
|
|
30
30
|
|
|
31
31
|
def test_help_flag(self):
|
|
32
32
|
"""Test that --help returns help text."""
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
"""Tests for .env file loading at CLI startup."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import subprocess
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TestEnvFileLoading:
|
|
9
|
+
"""Tests for automatic .env loading."""
|
|
10
|
+
|
|
11
|
+
def test_env_var_available_via_cli_check(self, tmp_path):
|
|
12
|
+
"""Verify .env file is loaded when CLI commands run."""
|
|
13
|
+
# Create .env with OPENAI_API_KEY
|
|
14
|
+
(tmp_path / ".env").write_text("OPENAI_API_KEY=sk-test-env-loading\n")
|
|
15
|
+
|
|
16
|
+
# Run check command which validates OPENAI_API_KEY
|
|
17
|
+
# Remove OPENAI_API_KEY from environment to ensure it comes from .env
|
|
18
|
+
env = {k: v for k, v in os.environ.items() if k != "OPENAI_API_KEY"}
|
|
19
|
+
env["PATH"] = os.environ.get("PATH", "")
|
|
20
|
+
|
|
21
|
+
result = subprocess.run(
|
|
22
|
+
[sys.executable, "-m", "adversarial_workflow.cli", "check"],
|
|
23
|
+
capture_output=True,
|
|
24
|
+
text=True,
|
|
25
|
+
cwd=tmp_path,
|
|
26
|
+
env=env,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
# The check command should see the API key from .env
|
|
30
|
+
# It will show as valid (green checkmark) in the output
|
|
31
|
+
combined_output = result.stdout + result.stderr
|
|
32
|
+
assert "OPENAI_API_KEY" in combined_output, (
|
|
33
|
+
f"Expected OPENAI_API_KEY check. stdout: {result.stdout}, stderr: {result.stderr}"
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
def test_env_loaded_before_evaluator_commands(self, tmp_path, monkeypatch):
|
|
37
|
+
"""API keys in .env are available to evaluator commands."""
|
|
38
|
+
# Create .env with test key
|
|
39
|
+
(tmp_path / ".env").write_text("TEST_API_KEY=secret-test-value\n")
|
|
40
|
+
|
|
41
|
+
# Create minimal evaluator config
|
|
42
|
+
eval_dir = tmp_path / ".adversarial" / "evaluators"
|
|
43
|
+
eval_dir.mkdir(parents=True)
|
|
44
|
+
(eval_dir / "test.yml").write_text("""name: test
|
|
45
|
+
description: Test evaluator
|
|
46
|
+
model: gpt-4o-mini
|
|
47
|
+
api_key_env: TEST_API_KEY
|
|
48
|
+
prompt: Test prompt
|
|
49
|
+
output_suffix: TEST
|
|
50
|
+
""")
|
|
51
|
+
|
|
52
|
+
monkeypatch.chdir(tmp_path)
|
|
53
|
+
# Ensure key is NOT in current environment
|
|
54
|
+
monkeypatch.delenv("TEST_API_KEY", raising=False)
|
|
55
|
+
|
|
56
|
+
# list-evaluators should work (loads .env, discovers evaluator)
|
|
57
|
+
result = subprocess.run(
|
|
58
|
+
[sys.executable, "-m", "adversarial_workflow.cli", "list-evaluators"],
|
|
59
|
+
capture_output=True,
|
|
60
|
+
text=True,
|
|
61
|
+
cwd=tmp_path,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
assert result.returncode == 0
|
|
65
|
+
assert "test" in result.stdout
|
|
66
|
+
|
|
67
|
+
def test_env_loaded_for_builtin_commands(self, tmp_path, monkeypatch):
|
|
68
|
+
""".env is loaded even for built-in commands."""
|
|
69
|
+
# Create .env with OpenAI key
|
|
70
|
+
(tmp_path / ".env").write_text("OPENAI_API_KEY=sk-test-key\n")
|
|
71
|
+
|
|
72
|
+
monkeypatch.chdir(tmp_path)
|
|
73
|
+
monkeypatch.delenv("OPENAI_API_KEY", raising=False)
|
|
74
|
+
|
|
75
|
+
# check command should find the key from .env
|
|
76
|
+
result = subprocess.run(
|
|
77
|
+
[sys.executable, "-m", "adversarial_workflow.cli", "check"],
|
|
78
|
+
capture_output=True,
|
|
79
|
+
text=True,
|
|
80
|
+
cwd=tmp_path,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# Should mention OpenAI (found from .env)
|
|
84
|
+
# The check may fail for other reasons but should see the key
|
|
85
|
+
assert "OPENAI" in result.stdout or "openai" in result.stdout.lower()
|
|
86
|
+
|
|
87
|
+
def test_missing_env_file_no_error(self, tmp_path, monkeypatch):
|
|
88
|
+
"""CLI works fine when no .env file exists."""
|
|
89
|
+
monkeypatch.chdir(tmp_path)
|
|
90
|
+
|
|
91
|
+
result = subprocess.run(
|
|
92
|
+
[sys.executable, "-m", "adversarial_workflow.cli", "--help"],
|
|
93
|
+
capture_output=True,
|
|
94
|
+
text=True,
|
|
95
|
+
cwd=tmp_path,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
assert result.returncode == 0
|
|
99
|
+
assert "adversarial" in result.stdout.lower()
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class TestCheckEnvCount:
|
|
103
|
+
"""Tests for check() command .env variable counting (ADV-0022).
|
|
104
|
+
|
|
105
|
+
These are CLI integration tests using subprocess to verify end-to-end behavior.
|
|
106
|
+
They test that check() correctly reports .env variable count even after
|
|
107
|
+
main() has already loaded the .env file at startup.
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
def test_check_reports_correct_env_count(self, tmp_path):
|
|
111
|
+
"""check() reports correct .env variable count even after main() loads it.
|
|
112
|
+
|
|
113
|
+
This is the primary regression test for ADV-0022. Before the fix,
|
|
114
|
+
check() would report "0 variables" because main() already loaded them.
|
|
115
|
+
"""
|
|
116
|
+
# Create .env with 3 variables
|
|
117
|
+
(tmp_path / ".env").write_text(
|
|
118
|
+
"OPENAI_API_KEY=sk-test\n"
|
|
119
|
+
"ANTHROPIC_API_KEY=ant-test\n"
|
|
120
|
+
"CUSTOM_KEY=custom-value\n"
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
# Remove keys from environment to isolate test
|
|
124
|
+
env = {k: v for k, v in os.environ.items()
|
|
125
|
+
if k not in ("OPENAI_API_KEY", "ANTHROPIC_API_KEY", "CUSTOM_KEY")}
|
|
126
|
+
env["PATH"] = os.environ.get("PATH", "")
|
|
127
|
+
|
|
128
|
+
result = subprocess.run(
|
|
129
|
+
[sys.executable, "-m", "adversarial_workflow.cli", "check"],
|
|
130
|
+
capture_output=True,
|
|
131
|
+
text=True,
|
|
132
|
+
cwd=tmp_path,
|
|
133
|
+
env=env,
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
# Should report "3 variables configured", not "0 variables"
|
|
137
|
+
assert "3 variables" in result.stdout, (
|
|
138
|
+
f"Expected '3 variables' in output. Got: {result.stdout}"
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
def test_check_handles_empty_env_file(self, tmp_path):
|
|
142
|
+
"""check() handles empty .env file gracefully."""
|
|
143
|
+
(tmp_path / ".env").write_text("")
|
|
144
|
+
|
|
145
|
+
env = {k: v for k, v in os.environ.items()}
|
|
146
|
+
env["PATH"] = os.environ.get("PATH", "")
|
|
147
|
+
|
|
148
|
+
result = subprocess.run(
|
|
149
|
+
[sys.executable, "-m", "adversarial_workflow.cli", "check"],
|
|
150
|
+
capture_output=True,
|
|
151
|
+
text=True,
|
|
152
|
+
cwd=tmp_path,
|
|
153
|
+
env=env,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
# Should report "0 variables configured"
|
|
157
|
+
assert "0 variables" in result.stdout, (
|
|
158
|
+
f"Expected '0 variables' in output. Got: {result.stdout}"
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
def test_check_handles_comments_in_env(self, tmp_path):
|
|
162
|
+
"""check() correctly counts variables, ignoring comments and empty lines."""
|
|
163
|
+
(tmp_path / ".env").write_text(
|
|
164
|
+
"# This is a comment\n"
|
|
165
|
+
"KEY1=value1\n"
|
|
166
|
+
"\n" # Empty line
|
|
167
|
+
"# Another comment\n"
|
|
168
|
+
"KEY2=value2\n"
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
env = {k: v for k, v in os.environ.items() if k not in ("KEY1", "KEY2")}
|
|
172
|
+
env["PATH"] = os.environ.get("PATH", "")
|
|
173
|
+
|
|
174
|
+
result = subprocess.run(
|
|
175
|
+
[sys.executable, "-m", "adversarial_workflow.cli", "check"],
|
|
176
|
+
capture_output=True,
|
|
177
|
+
text=True,
|
|
178
|
+
cwd=tmp_path,
|
|
179
|
+
env=env,
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
# Should report 2 variables (comments and empty lines ignored)
|
|
183
|
+
assert "2 variables" in result.stdout, (
|
|
184
|
+
f"Expected '2 variables' in output. Got: {result.stdout}"
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
def test_check_handles_unusual_env_entries(self, tmp_path):
|
|
188
|
+
"""check() handles unusual .env entries without crashing.
|
|
189
|
+
|
|
190
|
+
dotenv_values() treats 'KEY' without = as key with None value.
|
|
191
|
+
This test verifies the CLI doesn't crash on such inputs.
|
|
192
|
+
"""
|
|
193
|
+
(tmp_path / ".env").write_text(
|
|
194
|
+
"VALID_KEY=value\n"
|
|
195
|
+
"ALSO_VALID=another\n"
|
|
196
|
+
"KEY_WITHOUT_VALUE\n" # dotenv treats this as KEY_WITHOUT_VALUE=None
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
env = {k: v for k, v in os.environ.items()
|
|
200
|
+
if k not in ("VALID_KEY", "ALSO_VALID", "KEY_WITHOUT_VALUE")}
|
|
201
|
+
env["PATH"] = os.environ.get("PATH", "")
|
|
202
|
+
|
|
203
|
+
result = subprocess.run(
|
|
204
|
+
[sys.executable, "-m", "adversarial_workflow.cli", "check"],
|
|
205
|
+
capture_output=True,
|
|
206
|
+
text=True,
|
|
207
|
+
cwd=tmp_path,
|
|
208
|
+
env=env,
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
# Should not crash - dotenv_values() returns 3 entries (including key with None value)
|
|
212
|
+
assert "3 variables" in result.stdout, (
|
|
213
|
+
f"Expected '3 variables' in output. Got: {result.stdout}"
|
|
214
|
+
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow/evaluators/config.py
RENAMED
|
File without changes
|
|
File without changes
|
{adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow/evaluators/runner.py
RENAMED
|
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
|
{adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow/utils/__init__.py
RENAMED
|
File without changes
|
{adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow/utils/colors.py
RENAMED
|
File without changes
|
{adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow/utils/config.py
RENAMED
|
File without changes
|
|
File without changes
|
{adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow/utils/validation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/adversarial_workflow.egg-info/requires.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{adversarial_workflow-0.6.0 → adversarial_workflow-0.6.1}/tests/test_cli_dynamic_commands.py
RENAMED
|
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
|