adversarial-workflow 0.6.2__py3-none-any.whl → 0.6.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,5 @@
1
1
  """Allow execution via python -m adversarial_workflow."""
2
+
2
3
  from .cli import main
3
4
 
4
5
  if __name__ == "__main__":
@@ -322,16 +322,20 @@ def init_interactive(project_path: str = ".") -> int:
322
322
  f"{GREEN}✅ Setup Complete!{RESET}",
323
323
  [
324
324
  "Created:",
325
- " ✓ .env (with your API keys - added to .gitignore)"
326
- if (anthropic_key or openai_key)
327
- else " ⚠️ .env (skipped - no API keys provided)",
325
+ (
326
+ " ✓ .env (with your API keys - added to .gitignore)"
327
+ if (anthropic_key or openai_key)
328
+ else " ⚠️ .env (skipped - no API keys provided)"
329
+ ),
328
330
  " ✓ .adversarial/config.yml",
329
331
  " ✓ .adversarial/scripts/ (3 workflow scripts)",
330
332
  " ✓ .aider.conf.yml (aider configuration)",
331
333
  "",
332
- "Your configuration:"
333
- if (anthropic_key or openai_key)
334
- else "Configuration (no API keys yet):",
334
+ (
335
+ "Your configuration:"
336
+ if (anthropic_key or openai_key)
337
+ else "Configuration (no API keys yet):"
338
+ ),
335
339
  f" Author (implementation): {'Claude 3.5 Sonnet (Anthropic)' if anthropic_key else 'GPT-4o (OpenAI)' if openai_key else 'Not configured'}",
336
340
  f" Evaluator: {'GPT-4o (OpenAI)' if openai_key else 'Claude 3.5 Sonnet (Anthropic)' if anthropic_key else 'Not configured'}",
337
341
  f" Cost per workflow: {'~$0.02-0.10' if (anthropic_key and openai_key) else '~$0.05-0.15' if (anthropic_key or openai_key) else 'N/A'}",
@@ -2284,7 +2288,9 @@ def fetch_agent_template(url: str, template_type: str = "standard") -> Optional[
2284
2288
  )
2285
2289
  return None
2286
2290
  else:
2287
- print(f"{RED}❌ ERROR: {template_type} template not found in package{RESET}")
2291
+ print(
2292
+ f"{RED}❌ ERROR: {template_type} template not found in package{RESET}"
2293
+ )
2288
2294
  return None
2289
2295
 
2290
2296
  elif template_type == "custom" and url:
@@ -3082,8 +3088,8 @@ For more information: https://github.com/movito/adversarial-workflow
3082
3088
  "--timeout",
3083
3089
  "-t",
3084
3090
  type=int,
3085
- default=180,
3086
- help="Timeout in seconds (default: 180)",
3091
+ default=None,
3092
+ help="Timeout in seconds (default: from evaluator config or 180, max: 600)",
3087
3093
  )
3088
3094
  # Store config for later execution
3089
3095
  eval_parser.set_defaults(evaluator_config=config)
@@ -3096,10 +3102,34 @@ For more information: https://github.com/movito/adversarial-workflow
3096
3102
 
3097
3103
  # Check for evaluator command first (has evaluator_config attribute)
3098
3104
  if hasattr(args, "evaluator_config"):
3105
+ # Determine timeout: CLI flag > YAML config > default (180s)
3106
+ if args.timeout is not None:
3107
+ timeout = args.timeout
3108
+ source = "CLI override"
3109
+ elif args.evaluator_config.timeout != 180:
3110
+ timeout = args.evaluator_config.timeout
3111
+ source = "evaluator config"
3112
+ else:
3113
+ timeout = args.evaluator_config.timeout # 180 (default)
3114
+ source = "default"
3115
+
3116
+ # Validate CLI timeout (consistent with YAML validation)
3117
+ if timeout <= 0:
3118
+ print(f"{RED}Error: Timeout must be positive (> 0), got {timeout}{RESET}")
3119
+ return 1
3120
+ if timeout > 600:
3121
+ print(
3122
+ f"{YELLOW}Warning: Timeout {timeout}s exceeds maximum (600s), clamping to 600s{RESET}"
3123
+ )
3124
+ timeout = 600
3125
+
3126
+ # Log actual timeout and source
3127
+ print(f"Using timeout: {timeout}s ({source})")
3128
+
3099
3129
  return run_evaluator(
3100
3130
  args.evaluator_config,
3101
3131
  args.file,
3102
- timeout=args.timeout,
3132
+ timeout=timeout,
3103
3133
  )
3104
3134
 
3105
3135
  # Execute static commands
@@ -26,6 +26,7 @@ class EvaluatorConfig:
26
26
  fallback_model: Fallback model if primary fails
27
27
  aliases: Alternative command names
28
28
  version: Evaluator version
29
+ timeout: Timeout in seconds (default: 180, max: 600)
29
30
  source: "builtin" or "local" (set internally)
30
31
  config_file: Path to YAML file if local (set internally)
31
32
  """
@@ -43,6 +44,7 @@ class EvaluatorConfig:
43
44
  fallback_model: str | None = None
44
45
  aliases: list[str] = field(default_factory=list)
45
46
  version: str = "1.0.0"
47
+ timeout: int = 180 # Timeout in seconds (default: 180, max: 600)
46
48
 
47
49
  # Metadata (set internally during discovery, not from YAML)
48
50
  source: str = "builtin"
@@ -122,6 +122,35 @@ def parse_evaluator_yaml(yml_file: Path) -> EvaluatorConfig:
122
122
  f"Field '{field}' must be a string, got {type(value).__name__}: {value!r}"
123
123
  )
124
124
 
125
+ # Validate timeout if present
126
+ if "timeout" in data:
127
+ timeout = data["timeout"]
128
+ # Handle null/empty values
129
+ if timeout is None or timeout == "":
130
+ raise EvaluatorParseError("Field 'timeout' cannot be null or empty")
131
+ # Check for bool before int (bool is subclass of int in Python)
132
+ # YAML parses 'yes'/'true' as True, 'no'/'false' as False
133
+ if isinstance(timeout, bool):
134
+ raise EvaluatorParseError(
135
+ f"Field 'timeout' must be an integer, got bool: {timeout!r}"
136
+ )
137
+ if not isinstance(timeout, int):
138
+ raise EvaluatorParseError(
139
+ f"Field 'timeout' must be an integer, got {type(timeout).__name__}: {timeout!r}"
140
+ )
141
+ # timeout=0 is invalid (does not disable timeout - use a large value instead)
142
+ if timeout <= 0:
143
+ raise EvaluatorParseError(
144
+ f"Field 'timeout' must be positive (> 0), got {timeout}"
145
+ )
146
+ if timeout > 600:
147
+ logger.warning(
148
+ "Timeout %ds exceeds maximum (600s), clamping to 600s in %s",
149
+ timeout,
150
+ yml_file.name,
151
+ )
152
+ data["timeout"] = 600
153
+
125
154
  # Filter to known fields only (log unknown fields)
126
155
  known_fields = {
127
156
  "name",
@@ -134,6 +163,7 @@ def parse_evaluator_yaml(yml_file: Path) -> EvaluatorConfig:
134
163
  "fallback_model",
135
164
  "aliases",
136
165
  "version",
166
+ "timeout",
137
167
  }
138
168
  unknown = set(data.keys()) - known_fields
139
169
  if unknown:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: adversarial-workflow
3
- Version: 0.6.2
3
+ Version: 0.6.3
4
4
  Summary: Multi-stage AI code review system preventing phantom work - Author/Evaluator pattern
5
5
  Author: Fredrik Matheson
6
6
  License: MIT
@@ -55,9 +55,30 @@ Evaluate proposals, sort out ideas, and prevent "phantom work" (AI claiming to i
55
55
  - 🎯 **Tool-agnostic**: Use with Claude Code, Cursor, Aider, manual coding, or any workflow
56
56
  - ✨ **Interactive onboarding**: Guided setup wizard gets you started in <5 minutes
57
57
 
58
- ## What's New in v0.6.0
58
+ ## What's New in v0.6.3
59
59
 
60
- 🔌 **Plugin Architecture** - Define custom evaluators without modifying the package:
60
+ ### Upgrade
61
+
62
+ ```bash
63
+ pip install --upgrade adversarial-workflow
64
+ ```
65
+
66
+ ### v0.6.3 - Configurable Timeouts
67
+
68
+ - **Per-evaluator timeout**: Add `timeout: 300` to evaluator YAML for slow models like Mistral Large
69
+ - **CLI override**: Use `--timeout 400` to override YAML config on-the-fly
70
+ - **Timeout logging**: See which timeout source is used (CLI/YAML/default)
71
+ - **Safety limits**: Maximum 600 seconds to prevent runaway processes
72
+
73
+ ### v0.6.2 - .env Loading & Stability
74
+
75
+ - **Automatic .env loading**: API keys in `.env` files are now loaded at CLI startup
76
+ - **Custom evaluator support**: Evaluators using `api_key_env: GEMINI_API_KEY` (or other keys) now work with `.env` files
77
+ - **Better diagnostics**: `adversarial check` correctly reports the number of variables loaded from `.env`
78
+
79
+ ### v0.6.0 - Plugin Architecture
80
+
81
+ 🔌 **Custom Evaluators** - Define your own evaluators without modifying the package:
61
82
 
62
83
  ```bash
63
84
  # Create a custom evaluator
@@ -459,6 +480,7 @@ Starting with v0.6.0, you can define project-specific evaluators without modifyi
459
480
  | `aliases` | No | Alternative command names |
460
481
  | `log_prefix` | No | CLI output prefix |
461
482
  | `fallback_model` | No | Fallback model if primary fails |
483
+ | `timeout` | No | Timeout in seconds (default: 180, max: 600) |
462
484
  | `version` | No | Evaluator version (default: 1.0.0) |
463
485
 
464
486
  ### Listing Available Evaluators
@@ -1,10 +1,10 @@
1
1
  adversarial_workflow/__init__.py,sha256=moTEp6nKU5F4B1YnJaSBmwhptkDP0ST5n--2hak9PRc,596
2
- adversarial_workflow/__main__.py,sha256=Ibb0CngDCh4mpCe8Zxnf3kyKnMddBxQy2JAk_kfTUMQ,119
3
- adversarial_workflow/cli.py,sha256=I9LM6MnfW-m1kXbF202l50-SeidLCyyF0Sk0sR-UFqk,110743
2
+ adversarial_workflow/__main__.py,sha256=iM2jmO5YCFpGxfWiEhIYi_SsxVa0hRIE-MB7J0EcN7Y,120
3
+ adversarial_workflow/cli.py,sha256=FxjoC3KVUiwbOF7mWNPe6Zrk82fQcqyE8SPi5bo3ntI,111802
4
4
  adversarial_workflow/evaluators/__init__.py,sha256=A9ZKUmjSMfyvEu6jDzYAFLxfkt_OQ4RGA10Bv_eO2i4,1267
5
5
  adversarial_workflow/evaluators/builtins.py,sha256=u5LokYLe8ruEW2tunhOQaNSkpcZ9Ee2IeTkaC0dZDSY,1102
6
- adversarial_workflow/evaluators/config.py,sha256=05qYPIiIpCxXBVJzs70WQQLxi8I7MedfhE_oydXEcq0,1520
7
- adversarial_workflow/evaluators/discovery.py,sha256=a8qTUsuJRdPUcVbt1zzEgLixmvJbHA7WfnYSNm8V5OY,6834
6
+ adversarial_workflow/evaluators/config.py,sha256=H_4vkto07rAqnz0qEYdzN_DH6WbvRPMIEdkEOFE58UI,1651
7
+ adversarial_workflow/evaluators/discovery.py,sha256=dPQ0dDy9anYjzLnG-V9gVrLkCVAVZ2tEE9dyFWqSvJc,8079
8
8
  adversarial_workflow/evaluators/runner.py,sha256=JPVeigjGF2fRDVJLcGyDEuy9pCIp-LjmVAZyucMbdCU,9310
9
9
  adversarial_workflow/templates/.aider.conf.yml.template,sha256=jT2jWIgsnmS3HLhoQWMTO3GV07bUcsT2keYw60jqiDw,183
10
10
  adversarial_workflow/templates/.env.example.template,sha256=TmTlcgz44uZqIbqgXqdfHMl-0vVn96F_EGNohClFkb8,1821
@@ -25,9 +25,9 @@ adversarial_workflow/utils/colors.py,sha256=uRrG6KfIDBLo0F5_vPwms9NCm9-x8YXBiyZ4
25
25
  adversarial_workflow/utils/config.py,sha256=NBoC_-YYukEVo6BgpX2cDyeqV-3tnn_sHNU9L1AuSLQ,1341
26
26
  adversarial_workflow/utils/file_splitter.py,sha256=-zSWgAZ71DfX6dBu15Y4M84NBbJzq-0ENktbBEp9zvQ,12409
27
27
  adversarial_workflow/utils/validation.py,sha256=ZiJxtm03kJXicfFTt0QZwpc9V_D8PkDOVYrJEDsafQI,2202
28
- adversarial_workflow-0.6.2.dist-info/licenses/LICENSE,sha256=M-dOQlre-NmicyPa55hYOJUW8roGpCKEgtq-z0z1KCA,1073
29
- adversarial_workflow-0.6.2.dist-info/METADATA,sha256=zdFc4h-9XPcgg6mn0SCnJNg3VFhYl7XyxI0egYhU2fY,29955
30
- adversarial_workflow-0.6.2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
31
- adversarial_workflow-0.6.2.dist-info/entry_points.txt,sha256=9H-iZ-yF1uKZ8P0G1suc6kWR0NvK7uPZJbhN7nvt1sE,62
32
- adversarial_workflow-0.6.2.dist-info/top_level.txt,sha256=8irutNxLRjUbTlzfAibIpz7_ovkkF2h8ES69NQpv24c,21
33
- adversarial_workflow-0.6.2.dist-info/RECORD,,
28
+ adversarial_workflow-0.6.3.dist-info/licenses/LICENSE,sha256=M-dOQlre-NmicyPa55hYOJUW8roGpCKEgtq-z0z1KCA,1073
29
+ adversarial_workflow-0.6.3.dist-info/METADATA,sha256=4dfW8_CURJEoooPFtdqtYu-R-BVj-SCC-AXg_teHklg,30835
30
+ adversarial_workflow-0.6.3.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
31
+ adversarial_workflow-0.6.3.dist-info/entry_points.txt,sha256=9H-iZ-yF1uKZ8P0G1suc6kWR0NvK7uPZJbhN7nvt1sE,62
32
+ adversarial_workflow-0.6.3.dist-info/top_level.txt,sha256=8irutNxLRjUbTlzfAibIpz7_ovkkF2h8ES69NQpv24c,21
33
+ adversarial_workflow-0.6.3.dist-info/RECORD,,