fluxloop-cli 0.3.4__tar.gz → 0.3.6__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 (70) hide show
  1. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/PKG-INFO +16 -2
  2. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/README.md +15 -1
  3. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/__init__.py +1 -1
  4. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/api_utils.py +32 -9
  5. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/commands/apikeys.py +4 -1
  6. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/commands/auth.py +6 -13
  7. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/commands/bundles.py +20 -5
  8. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/commands/config.py +3 -8
  9. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/commands/context.py +4 -1
  10. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/commands/data.py +24 -6
  11. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/commands/evaluate.py +4 -1
  12. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/commands/inputs.py +20 -5
  13. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/commands/personas.py +12 -3
  14. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/commands/projects.py +16 -4
  15. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/commands/scenarios.py +28 -7
  16. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/commands/sync.py +11 -13
  17. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/context_manager.py +3 -3
  18. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/templates.py +1 -2
  19. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli.egg-info/PKG-INFO +16 -2
  20. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/pyproject.toml +1 -1
  21. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/arg_binder.py +0 -0
  22. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/auth_manager.py +0 -0
  23. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/commands/__init__.py +0 -0
  24. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/commands/criteria.py +0 -0
  25. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/commands/generate.py +0 -0
  26. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/commands/init.py +0 -0
  27. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/commands/local_context.py +0 -0
  28. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/commands/run.py +0 -0
  29. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/commands/status.py +0 -0
  30. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/commands/test.py +0 -0
  31. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/common_options.py +0 -0
  32. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/config_loader.py +0 -0
  33. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/config_schema.py +0 -0
  34. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/constants.py +0 -0
  35. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/conversation_supervisor.py +0 -0
  36. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/environment.py +0 -0
  37. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/http_client.py +0 -0
  38. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/input_generator.py +0 -0
  39. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/llm_generator.py +0 -0
  40. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/main.py +0 -0
  41. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/project_paths.py +0 -0
  42. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/runner.py +0 -0
  43. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/target_loader.py +0 -0
  44. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/testing/__init__.py +0 -0
  45. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/testing/pytest_plugin.py +0 -0
  46. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/testing/types.py +0 -0
  47. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/token_usage.py +0 -0
  48. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/turns.py +0 -0
  49. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli/validators.py +0 -0
  50. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli.egg-info/SOURCES.txt +0 -0
  51. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli.egg-info/dependency_links.txt +0 -0
  52. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli.egg-info/entry_points.txt +0 -0
  53. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli.egg-info/requires.txt +0 -0
  54. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/fluxloop_cli.egg-info/top_level.txt +0 -0
  55. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/setup.cfg +0 -0
  56. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/tests/test_analysis_recommendations.py +0 -0
  57. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/tests/test_arg_binder.py +0 -0
  58. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/tests/test_config_command.py +0 -0
  59. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/tests/test_conversation_supervisor.py +0 -0
  60. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/tests/test_evaluate_command.py +0 -0
  61. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/tests/test_evaluation_llm.py +0 -0
  62. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/tests/test_init_pytest_template.py +0 -0
  63. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/tests/test_input_generator.py +0 -0
  64. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/tests/test_prompt_library.py +0 -0
  65. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/tests/test_pytest_plugin.py +0 -0
  66. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/tests/test_run_command.py +0 -0
  67. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/tests/test_runner_multi_turn.py +0 -0
  68. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/tests/test_success_criteria.py +0 -0
  69. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/tests/test_target_loader.py +0 -0
  70. {fluxloop_cli-0.3.4 → fluxloop_cli-0.3.6}/tests/test_testing_types.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fluxloop-cli
3
- Version: 0.3.4
3
+ Version: 0.3.6
4
4
  Summary: FluxLoop CLI for running agent simulations
5
5
  Author-email: FluxLoop Team <team@fluxloop.dev>
6
6
  License: Apache-2.0
@@ -128,6 +128,21 @@ Options:
128
128
  After running the script, open the folder in VSCode and use `FluxLoop: Show Environment Info`
129
129
  or `FluxLoop: Run Doctor` to confirm the environment.
130
130
 
131
+ ## Full Reset Script
132
+
133
+ To remove FluxLoop-only local state (`.fluxloop*`, `~/.fluxloop`), remove
134
+ project deps via `uv remove` (when `pyproject.toml` exists), and uninstall
135
+ `fluxloop-cli` / `fluxloop` from pipx/uv tool/pip:
136
+
137
+ ```bash
138
+ bash reset_fluxloop_env.sh --dry-run
139
+ bash reset_fluxloop_env.sh --yes
140
+ ```
141
+
142
+ Options:
143
+
144
+ - `--project-root PATH` - clean a specific project root
145
+
131
146
  ## Runner Integration Patterns
132
147
 
133
148
  Configure how FluxLoop calls your code in `configs/simulation.yaml`:
@@ -156,4 +171,3 @@ To package the CLI:
156
171
  ```
157
172
  ./build.sh
158
173
  ```
159
-
@@ -85,6 +85,21 @@ Options:
85
85
  After running the script, open the folder in VSCode and use `FluxLoop: Show Environment Info`
86
86
  or `FluxLoop: Run Doctor` to confirm the environment.
87
87
 
88
+ ## Full Reset Script
89
+
90
+ To remove FluxLoop-only local state (`.fluxloop*`, `~/.fluxloop`), remove
91
+ project deps via `uv remove` (when `pyproject.toml` exists), and uninstall
92
+ `fluxloop-cli` / `fluxloop` from pipx/uv tool/pip:
93
+
94
+ ```bash
95
+ bash reset_fluxloop_env.sh --dry-run
96
+ bash reset_fluxloop_env.sh --yes
97
+ ```
98
+
99
+ Options:
100
+
101
+ - `--project-root PATH` - clean a specific project root
102
+
88
103
  ## Runner Integration Patterns
89
104
 
90
105
  Configure how FluxLoop calls your code in `configs/simulation.yaml`:
@@ -113,4 +128,3 @@ To package the CLI:
113
128
  ```
114
129
  ./build.sh
115
130
  ```
116
-
@@ -2,7 +2,7 @@
2
2
  FluxLoop CLI - Command-line interface for running agent simulations.
3
3
  """
4
4
 
5
- __version__ = "0.3.4"
5
+ __version__ = "0.3.6"
6
6
 
7
7
  from .main import app
8
8
 
@@ -20,18 +20,41 @@ from rich.console import Console
20
20
  console = Console()
21
21
 
22
22
 
23
- def resolve_api_url(override: Optional[str]) -> str:
23
+ PRODUCTION_API_URL = "https://api.fluxloop.ai"
24
+ STAGING_API_URL = "https://staging.api.fluxloop.ai"
25
+
26
+
27
+ def _read_project_json_api_url() -> Optional[str]:
28
+ """Read api_url from .fluxloop/project.json if it exists."""
29
+ try:
30
+ from .context_manager import load_project_connection
31
+
32
+ conn = load_project_connection()
33
+ if conn and conn.api_url:
34
+ return conn.api_url
35
+ except Exception:
36
+ pass
37
+ return None
38
+
39
+
40
+ def resolve_api_url(
41
+ override: Optional[str] = None,
42
+ *,
43
+ staging: bool = False,
44
+ ) -> str:
24
45
  """
25
- Resolve API URL from override or environment variables.
46
+ Resolve API URL with a single, unified priority chain.
26
47
 
27
48
  Priority:
28
- 1. Override parameter
29
- 2. FLUXLOOP_API_URL
30
- 3. FLUXLOOP_SYNC_URL
31
- 4. Default: https://api.fluxloop.ai
49
+ 1. ``override`` parameter (--api-url)
50
+ 2. FLUXLOOP_API_URL environment variable
51
+ 3. .fluxloop/project.json ``api_url``
52
+ 4. ``--staging`` flag → staging URL
53
+ 5. Hardcoded production URL (final fallback)
32
54
 
33
55
  Args:
34
- override: Optional URL override.
56
+ override: Optional URL override (--api-url).
57
+ staging: When True, use staging URL as fallback instead of production.
35
58
 
36
59
  Returns:
37
60
  Resolved API URL (trailing slash removed).
@@ -39,8 +62,8 @@ def resolve_api_url(override: Optional[str]) -> str:
39
62
  url = (
40
63
  override
41
64
  or os.getenv("FLUXLOOP_API_URL")
42
- or os.getenv("FLUXLOOP_SYNC_URL")
43
- or "https://api.fluxloop.ai"
65
+ or _read_project_json_api_url()
66
+ or (STAGING_API_URL if staging else PRODUCTION_API_URL)
44
67
  )
45
68
  return url.rstrip("/")
46
69
 
@@ -109,13 +109,16 @@ def create_api_key(
109
109
  project: Optional[str] = typer.Option(None, "--project", help="Project name (legacy)"),
110
110
  root: Path = typer.Option(Path(DEFAULT_ROOT_DIR_NAME), "--root", help="Root dir"),
111
111
  api_url: Optional[str] = typer.Option(None, "--api-url", help="API base URL"),
112
+ staging: bool = typer.Option(
113
+ False, "--staging", help="Use staging API (staging.api.fluxloop.ai)"
114
+ ),
112
115
  ):
113
116
  """Create an API Key for sync operations (pull/upload).
114
117
 
115
118
  Saves to .fluxloop/.env (workspace level) by default.
116
119
  API keys are project-scoped and shared by all scenarios.
117
120
  """
118
- api_url = resolve_api_url(api_url)
121
+ api_url = resolve_api_url(api_url, staging=staging)
119
122
 
120
123
  # Priority: --env-file > workspace .fluxloop/.env > legacy resolution
121
124
  if env_file:
@@ -16,6 +16,7 @@ from rich.panel import Panel
16
16
  from rich.spinner import Spinner
17
17
  from rich.live import Live
18
18
 
19
+ from ..api_utils import resolve_api_url
19
20
  from ..auth_manager import (
20
21
  start_device_code_flow,
21
22
  poll_device_code,
@@ -35,17 +36,6 @@ app = typer.Typer(help="Manage FluxLoop authentication")
35
36
  console = Console()
36
37
 
37
38
 
38
- def _resolve_api_url(override: Optional[str]) -> str:
39
- """Resolve API URL from override or environment variables."""
40
- url = (
41
- override
42
- or os.getenv("FLUXLOOP_API_URL")
43
- or os.getenv("FLUXLOOP_SYNC_URL")
44
- or "https://api.fluxloop.ai"
45
- )
46
- return url.rstrip("/")
47
-
48
-
49
39
  def _is_agent_environment() -> bool:
50
40
  """Detect if running in an agent/headless environment."""
51
41
  # Check common agent environment indicators
@@ -63,6 +53,9 @@ def login(
63
53
  api_url: Optional[str] = typer.Option(
64
54
  None, "--api-url", help="FluxLoop API base URL"
65
55
  ),
56
+ staging: bool = typer.Option(
57
+ False, "--staging", help="Use staging API (staging.api.fluxloop.ai)"
58
+ ),
66
59
  force: bool = typer.Option(
67
60
  False, "--force", "-f", help="Force re-login even if already logged in"
68
61
  ),
@@ -87,7 +80,7 @@ def login(
87
80
  unless --force is specified. This makes the command idempotent and
88
81
  safe to call from automated scripts or AI agents.
89
82
  """
90
- api_url = _resolve_api_url(api_url)
83
+ api_url = resolve_api_url(api_url, staging=staging)
91
84
 
92
85
  # Check if already logged in (idempotent behavior)
93
86
  if not force:
@@ -290,7 +283,7 @@ def status():
290
283
  If token is expired or about to expire, automatically attempts to refresh.
291
284
  Only suggests re-login if refresh fails.
292
285
  """
293
- api_url = _resolve_api_url(None)
286
+ api_url = resolve_api_url()
294
287
  token = load_auth_token()
295
288
 
296
289
  if not token:
@@ -39,6 +39,9 @@ def publish(
39
39
  api_url: Optional[str] = typer.Option(
40
40
  None, "--api-url", help="FluxLoop API base URL"
41
41
  ),
42
+ staging: bool = typer.Option(
43
+ False, "--staging", help="Use staging API (staging.api.fluxloop.ai)"
44
+ ),
42
45
  ):
43
46
  """
44
47
  Create and publish a bundle (combined operation).
@@ -47,7 +50,7 @@ def publish(
47
50
  1. Creates a bundle with status 'draft'
48
51
  2. Updates status to 'published'
49
52
  """
50
- api_url = resolve_api_url(api_url)
53
+ api_url = resolve_api_url(api_url, staging=staging)
51
54
 
52
55
  # Use context if no project_id specified
53
56
  if not project_id:
@@ -153,11 +156,14 @@ def create(
153
156
  api_url: Optional[str] = typer.Option(
154
157
  None, "--api-url", help="FluxLoop API base URL"
155
158
  ),
159
+ staging: bool = typer.Option(
160
+ False, "--staging", help="Use staging API (staging.api.fluxloop.ai)"
161
+ ),
156
162
  ):
157
163
  """
158
164
  Create a new bundle (draft status).
159
165
  """
160
- api_url = resolve_api_url(api_url)
166
+ api_url = resolve_api_url(api_url, staging=staging)
161
167
 
162
168
  # Use context if no project_id specified
163
169
  if not project_id:
@@ -223,11 +229,14 @@ def list_bundles(
223
229
  api_url: Optional[str] = typer.Option(
224
230
  None, "--api-url", help="FluxLoop API base URL"
225
231
  ),
232
+ staging: bool = typer.Option(
233
+ False, "--staging", help="Use staging API (staging.api.fluxloop.ai)"
234
+ ),
226
235
  ):
227
236
  """
228
237
  List all bundles.
229
238
  """
230
- api_url = resolve_api_url(api_url)
239
+ api_url = resolve_api_url(api_url, staging=staging)
231
240
 
232
241
  # Use context if no project_id specified
233
242
  if not project_id:
@@ -298,11 +307,14 @@ def show(
298
307
  api_url: Optional[str] = typer.Option(
299
308
  None, "--api-url", help="FluxLoop API base URL"
300
309
  ),
310
+ staging: bool = typer.Option(
311
+ False, "--staging", help="Use staging API (staging.api.fluxloop.ai)"
312
+ ),
301
313
  ):
302
314
  """
303
315
  Show bundle details.
304
316
  """
305
- api_url = resolve_api_url(api_url)
317
+ api_url = resolve_api_url(api_url, staging=staging)
306
318
 
307
319
  try:
308
320
  client = create_authenticated_client(api_url, use_jwt=True)
@@ -367,11 +379,14 @@ def update(
367
379
  api_url: Optional[str] = typer.Option(
368
380
  None, "--api-url", help="FluxLoop API base URL"
369
381
  ),
382
+ staging: bool = typer.Option(
383
+ False, "--staging", help="Use staging API (staging.api.fluxloop.ai)"
384
+ ),
370
385
  ):
371
386
  """
372
387
  Update bundle fields.
373
388
  """
374
- api_url = resolve_api_url(api_url)
389
+ api_url = resolve_api_url(api_url, staging=staging)
375
390
 
376
391
  # Build payload
377
392
  payload: Dict[str, Any] = {}
@@ -191,8 +191,7 @@ def env(
191
191
  loaded_paths = []
192
192
 
193
193
  env_vars = [
194
- ("FLUXLOOP_SYNC_URL", "Sync API base URL", "https://api.fluxloop.ai"),
195
- ("FLUXLOOP_API_KEY", "API key for authentication", None),
194
+ ("FLUXLOOP_API_URL", "API base URL override (optional, project.json is primary)", None),
196
195
  ("FLUXLOOP_SYNC_API_KEY", "Sync API key for uploads", None),
197
196
  ("FLUXLOOP_ENABLED", "Enable/disable tracing", "true"),
198
197
  ("FLUXLOOP_DEBUG", "Enable debug mode", "false"),
@@ -327,13 +326,12 @@ def set_llm(
327
326
  @app.command()
328
327
  def set_sync_key(
329
328
  api_key: str = typer.Argument(..., help="Sync API key"),
330
- api_url: Optional[str] = typer.Option(None, "--url", help="Sync API base URL"),
331
329
  overwrite_env: bool = typer.Option(False, "--overwrite-env", help="Overwrite existing key in .env"),
332
330
  env_file: Path = typer.Option(Path(".env"), "--env-file", help="Path to environment file"),
333
331
  project: Optional[str] = typer.Option(None, "--project", help="Project name under the FluxLoop root"),
334
332
  root: Path = typer.Option(Path(DEFAULT_ROOT_DIR_NAME), "--root", help="FluxLoop root directory"),
335
333
  ):
336
- """Save Sync API credentials to .env."""
334
+ """Save Sync API key to .env."""
337
335
 
338
336
  env_path = resolve_env_path(env_file, project, root)
339
337
  env_contents: Dict[str, str] = {}
@@ -351,12 +349,9 @@ def set_sync_key(
351
349
  else:
352
350
  env_contents["FLUXLOOP_SYNC_API_KEY"] = api_key
353
351
 
354
- if api_url:
355
- env_contents["FLUXLOOP_SYNC_URL"] = api_url
356
-
357
352
  env_lines = [f"{key}={value}" for key, value in env_contents.items()]
358
353
  env_path.write_text("\n".join(env_lines) + "\n")
359
- console.print(f"[green]✓[/green] Saved sync credentials to {env_path}")
354
+ console.print(f"[green]✓[/green] Saved sync API key to {env_path}")
360
355
 
361
356
 
362
357
  @app.command()
@@ -41,13 +41,16 @@ def refine(
41
41
  api_url: Optional[str] = typer.Option(
42
42
  None, "--api-url", help="FluxLoop API base URL"
43
43
  ),
44
+ staging: bool = typer.Option(
45
+ False, "--staging", help="Use staging API (staging.api.fluxloop.ai)"
46
+ ),
44
47
  ):
45
48
  """
46
49
  Refine intent and extract constraints using AI.
47
50
 
48
51
  Uses current project from context if --project-id is not specified.
49
52
  """
50
- api_url = resolve_api_url(api_url)
53
+ api_url = resolve_api_url(api_url, staging=staging)
51
54
 
52
55
  # Use context if no project_id specified
53
56
  if not project_id:
@@ -108,6 +108,9 @@ def push(
108
108
  "--api-url",
109
109
  help="FluxLoop API base URL",
110
110
  ),
111
+ staging: bool = typer.Option(
112
+ False, "--staging", help="Use staging API (staging.api.fluxloop.ai)"
113
+ ),
111
114
  quiet: bool = typer.Option(False, "--quiet", "-q", help="Minimal output"),
112
115
  ):
113
116
  """
@@ -139,7 +142,7 @@ def push(
139
142
  raise typer.Exit(1)
140
143
 
141
144
  # Resolve project
142
- api_url = resolve_api_url(api_url)
145
+ api_url = resolve_api_url(api_url, staging=staging)
143
146
  if not project_id:
144
147
  project_id = get_current_web_project_id()
145
148
  if not project_id:
@@ -293,13 +296,16 @@ def list_data(
293
296
  "--api-url",
294
297
  help="FluxLoop API base URL",
295
298
  ),
299
+ staging: bool = typer.Option(
300
+ False, "--staging", help="Use staging API (staging.api.fluxloop.ai)"
301
+ ),
296
302
  ):
297
303
  """
298
304
  List all data in the project library.
299
305
 
300
306
  Shows both KNOWLEDGE (documents) and DATASET entries with their processing status.
301
307
  """
302
- api_url = resolve_api_url(api_url)
308
+ api_url = resolve_api_url(api_url, staging=staging)
303
309
 
304
310
  if not project_id:
305
311
  project_id = get_current_web_project_id()
@@ -390,11 +396,14 @@ def show(
390
396
  "--api-url",
391
397
  help="FluxLoop API base URL",
392
398
  ),
399
+ staging: bool = typer.Option(
400
+ False, "--staging", help="Use staging API (staging.api.fluxloop.ai)"
401
+ ),
393
402
  ):
394
403
  """
395
404
  Show details of a specific data record.
396
405
  """
397
- api_url = resolve_api_url(api_url)
406
+ api_url = resolve_api_url(api_url, staging=staging)
398
407
 
399
408
  if not project_id:
400
409
  project_id = get_current_web_project_id()
@@ -498,6 +507,9 @@ def bind(
498
507
  "--api-url",
499
508
  help="FluxLoop API base URL",
500
509
  ),
510
+ staging: bool = typer.Option(
511
+ False, "--staging", help="Use staging API (staging.api.fluxloop.ai)"
512
+ ),
501
513
  ):
502
514
  """
503
515
  Bind project data to a scenario.
@@ -505,7 +517,7 @@ def bind(
505
517
  Creates an association between data in the project library and a scenario,
506
518
  allowing the scenario to use this data for testing.
507
519
  """
508
- api_url = resolve_api_url(api_url)
520
+ api_url = resolve_api_url(api_url, staging=staging)
509
521
 
510
522
  if not scenario_id:
511
523
  scenario_id = get_current_scenario_id()
@@ -558,11 +570,14 @@ def unbind(
558
570
  "--api-url",
559
571
  help="FluxLoop API base URL",
560
572
  ),
573
+ staging: bool = typer.Option(
574
+ False, "--staging", help="Use staging API (staging.api.fluxloop.ai)"
575
+ ),
561
576
  ):
562
577
  """
563
578
  Remove a data binding from a scenario.
564
579
  """
565
- api_url = resolve_api_url(api_url)
580
+ api_url = resolve_api_url(api_url, staging=staging)
566
581
 
567
582
  if not scenario_id:
568
583
  scenario_id = get_current_scenario_id()
@@ -607,6 +622,9 @@ def reprocess(
607
622
  "--api-url",
608
623
  help="FluxLoop API base URL",
609
624
  ),
625
+ staging: bool = typer.Option(
626
+ False, "--staging", help="Use staging API (staging.api.fluxloop.ai)"
627
+ ),
610
628
  ):
611
629
  """
612
630
  Reprocess data with a different category or to fix processing errors.
@@ -615,7 +633,7 @@ def reprocess(
615
633
  - Initial processing failed
616
634
  - You want to change the data category (document ↔ dataset)
617
635
  """
618
- api_url = resolve_api_url(api_url)
636
+ api_url = resolve_api_url(api_url, staging=staging)
619
637
 
620
638
  if not project_id:
621
639
  project_id = get_current_web_project_id()
@@ -57,6 +57,9 @@ def main(
57
57
  api_url: Optional[str] = typer.Option(
58
58
  None, "--api-url", help="FluxLoop API base URL"
59
59
  ),
60
+ staging: bool = typer.Option(
61
+ False, "--staging", help="Use staging API (staging.api.fluxloop.ai)"
62
+ ),
60
63
  ):
61
64
  """
62
65
  Trigger server-side evaluation for an experiment.
@@ -64,7 +67,7 @@ def main(
64
67
  Uses the current logged-in user (JWT). If no config is provided, the server
65
68
  will auto-select or use scenario defaults.
66
69
  """
67
- api_url = resolve_api_url(api_url)
70
+ api_url = resolve_api_url(api_url, staging=staging)
68
71
 
69
72
  if not project_id:
70
73
  project_id = get_current_web_project_id()
@@ -85,6 +85,9 @@ def synthesize(
85
85
  api_url: Optional[str] = typer.Option(
86
86
  None, "--api-url", help="FluxLoop API base URL"
87
87
  ),
88
+ staging: bool = typer.Option(
89
+ False, "--staging", help="Use staging API (staging.api.fluxloop.ai)"
90
+ ),
88
91
  ):
89
92
  """
90
93
  Synthesize test inputs using Web API.
@@ -92,7 +95,7 @@ def synthesize(
92
95
  Uses current project/scenario from context if not specified.
93
96
  """
94
97
  effective_timeout = timeout_seconds or _get_default_timeout()
95
- api_url = resolve_api_url(api_url)
98
+ api_url = resolve_api_url(api_url, staging=staging)
96
99
 
97
100
  # Use context if no project_id specified
98
101
  if not project_id:
@@ -275,11 +278,14 @@ def refine(
275
278
  api_url: Optional[str] = typer.Option(
276
279
  None, "--api-url", help="FluxLoop API base URL"
277
280
  ),
281
+ staging: bool = typer.Option(
282
+ False, "--staging", help="Use staging API (staging.api.fluxloop.ai)"
283
+ ),
278
284
  ):
279
285
  """
280
286
  Refine existing test inputs.
281
287
  """
282
- api_url = resolve_api_url(api_url)
288
+ api_url = resolve_api_url(api_url, staging=staging)
283
289
  effective_timeout = timeout_seconds or _get_default_timeout()
284
290
 
285
291
  # Build payload
@@ -361,11 +367,14 @@ def qc(
361
367
  api_url: Optional[str] = typer.Option(
362
368
  None, "--api-url", help="FluxLoop API base URL"
363
369
  ),
370
+ staging: bool = typer.Option(
371
+ False, "--staging", help="Use staging API (staging.api.fluxloop.ai)"
372
+ ),
364
373
  ):
365
374
  """
366
375
  Run quality checks on test inputs.
367
376
  """
368
- api_url = resolve_api_url(api_url)
377
+ api_url = resolve_api_url(api_url, staging=staging)
369
378
  effective_timeout = timeout_seconds or _get_default_timeout()
370
379
 
371
380
  # Build payload
@@ -456,11 +465,14 @@ def list_inputs(
456
465
  api_url: Optional[str] = typer.Option(
457
466
  None, "--api-url", help="FluxLoop API base URL"
458
467
  ),
468
+ staging: bool = typer.Option(
469
+ False, "--staging", help="Use staging API (staging.api.fluxloop.ai)"
470
+ ),
459
471
  ):
460
472
  """
461
473
  List all input sets.
462
474
  """
463
- api_url = resolve_api_url(api_url)
475
+ api_url = resolve_api_url(api_url, staging=staging)
464
476
 
465
477
  # Use context if no project_id specified
466
478
  if not project_id:
@@ -525,11 +537,14 @@ def show(
525
537
  api_url: Optional[str] = typer.Option(
526
538
  None, "--api-url", help="FluxLoop API base URL"
527
539
  ),
540
+ staging: bool = typer.Option(
541
+ False, "--staging", help="Use staging API (staging.api.fluxloop.ai)"
542
+ ),
528
543
  ):
529
544
  """
530
545
  Show input set details with samples.
531
546
  """
532
- api_url = resolve_api_url(api_url)
547
+ api_url = resolve_api_url(api_url, staging=staging)
533
548
 
534
549
  try:
535
550
  client = create_authenticated_client(api_url, use_jwt=True)
@@ -35,13 +35,16 @@ def suggest(
35
35
  api_url: Optional[str] = typer.Option(
36
36
  None, "--api-url", help="FluxLoop API base URL"
37
37
  ),
38
+ staging: bool = typer.Option(
39
+ False, "--staging", help="Use staging API (staging.api.fluxloop.ai)"
40
+ ),
38
41
  ):
39
42
  """
40
43
  Get AI-suggested personas for a scenario.
41
44
 
42
45
  Uses current project from context if --project-id is not specified.
43
46
  """
44
- api_url = resolve_api_url(api_url)
47
+ api_url = resolve_api_url(api_url, staging=staging)
45
48
 
46
49
  # Use context if no project_id specified
47
50
  if not project_id:
@@ -150,11 +153,14 @@ def list_personas(
150
153
  api_url: Optional[str] = typer.Option(
151
154
  None, "--api-url", help="FluxLoop API base URL"
152
155
  ),
156
+ staging: bool = typer.Option(
157
+ False, "--staging", help="Use staging API (staging.api.fluxloop.ai)"
158
+ ),
153
159
  ):
154
160
  """
155
161
  List all personas.
156
162
  """
157
- api_url = resolve_api_url(api_url)
163
+ api_url = resolve_api_url(api_url, staging=staging)
158
164
 
159
165
  try:
160
166
  client = create_authenticated_client(api_url, use_jwt=True)
@@ -220,11 +226,14 @@ def create(
220
226
  api_url: Optional[str] = typer.Option(
221
227
  None, "--api-url", help="FluxLoop API base URL"
222
228
  ),
229
+ staging: bool = typer.Option(
230
+ False, "--staging", help="Use staging API (staging.api.fluxloop.ai)"
231
+ ),
223
232
  ):
224
233
  """
225
234
  Create a custom persona.
226
235
  """
227
- api_url = resolve_api_url(api_url)
236
+ api_url = resolve_api_url(api_url, staging=staging)
228
237
 
229
238
  # Build payload
230
239
  payload: Dict[str, Any] = {"name": name}
@@ -54,13 +54,16 @@ def list_projects(
54
54
  api_url: Optional[str] = typer.Option(
55
55
  None, "--api-url", help="FluxLoop API base URL"
56
56
  ),
57
+ staging: bool = typer.Option(
58
+ False, "--staging", help="Use staging API (staging.api.fluxloop.ai)"
59
+ ),
57
60
  ):
58
61
  """
59
62
  List all Web Projects you have access to.
60
63
 
61
64
  Shows which project is currently selected for this workspace.
62
65
  """
63
- api_url = resolve_api_url(api_url)
66
+ api_url = resolve_api_url(api_url, staging=staging)
64
67
 
65
68
  try:
66
69
  client = create_authenticated_client(api_url, use_jwt=True)
@@ -120,6 +123,9 @@ def create_project(
120
123
  api_url: Optional[str] = typer.Option(
121
124
  None, "--api-url", help="FluxLoop API base URL"
122
125
  ),
126
+ staging: bool = typer.Option(
127
+ False, "--staging", help="Use staging API (staging.api.fluxloop.ai)"
128
+ ),
123
129
  select: bool = typer.Option(
124
130
  True, "--select/--no-select", help="Automatically select the created project"
125
131
  ),
@@ -127,7 +133,7 @@ def create_project(
127
133
  """
128
134
  Create a new Web Project.
129
135
  """
130
- api_url = resolve_api_url(api_url)
136
+ api_url = resolve_api_url(api_url, staging=staging)
131
137
 
132
138
  try:
133
139
  client = create_authenticated_client(api_url, use_jwt=True)
@@ -165,6 +171,9 @@ def select_project(
165
171
  api_url: Optional[str] = typer.Option(
166
172
  None, "--api-url", help="FluxLoop API base URL"
167
173
  ),
174
+ staging: bool = typer.Option(
175
+ False, "--staging", help="Use staging API (staging.api.fluxloop.ai)"
176
+ ),
168
177
  ):
169
178
  """
170
179
  Select a Web Project for this workspace.
@@ -176,7 +185,7 @@ def select_project(
176
185
  Use this to connect your local workspace to a Web Project before
177
186
  creating scenarios with 'fluxloop init scenario'.
178
187
  """
179
- api_url = resolve_api_url(api_url)
188
+ api_url = resolve_api_url(api_url, staging=staging)
180
189
 
181
190
  try:
182
191
  # Verify project exists by fetching it
@@ -212,11 +221,14 @@ def show_project(
212
221
  api_url: Optional[str] = typer.Option(
213
222
  None, "--api-url", help="FluxLoop API base URL"
214
223
  ),
224
+ staging: bool = typer.Option(
225
+ False, "--staging", help="Use staging API (staging.api.fluxloop.ai)"
226
+ ),
215
227
  ):
216
228
  """
217
229
  Show details of a project.
218
230
  """
219
- api_url = resolve_api_url(api_url)
231
+ api_url = resolve_api_url(api_url, staging=staging)
220
232
 
221
233
  # Use current context if no project_id provided
222
234
  if not project_id:
@@ -45,13 +45,16 @@ def refine(
45
45
  api_url: Optional[str] = typer.Option(
46
46
  None, "--api-url", help="FluxLoop API base URL"
47
47
  ),
48
+ staging: bool = typer.Option(
49
+ False, "--staging", help="Use staging API (staging.api.fluxloop.ai)"
50
+ ),
48
51
  ):
49
52
  """
50
53
  Refine scenario goal and constraints using AI.
51
54
 
52
55
  Uses current project from context if --project-id is not specified.
53
56
  """
54
- api_url = resolve_api_url(api_url)
57
+ api_url = resolve_api_url(api_url, staging=staging)
55
58
 
56
59
  # Use context if no project_id specified
57
60
  if not project_id:
@@ -136,6 +139,9 @@ def create(
136
139
  api_url: Optional[str] = typer.Option(
137
140
  None, "--api-url", help="FluxLoop API base URL"
138
141
  ),
142
+ staging: bool = typer.Option(
143
+ False, "--staging", help="Use staging API (staging.api.fluxloop.ai)"
144
+ ),
139
145
  select: bool = typer.Option(
140
146
  True, "--select/--no-select", help="Automatically select the created scenario"
141
147
  ),
@@ -163,7 +169,7 @@ def create(
163
169
  --constraint "Response time under 30 seconds" \\
164
170
  --assumption "User has no SQL knowledge"
165
171
  """
166
- api_url = resolve_api_url(api_url)
172
+ api_url = resolve_api_url(api_url, staging=staging)
167
173
 
168
174
  # Use context if no project_id specified
169
175
  if not project_id:
@@ -252,11 +258,14 @@ def generate(
252
258
  api_url: Optional[str] = typer.Option(
253
259
  None, "--api-url", help="FluxLoop API base URL"
254
260
  ),
261
+ staging: bool = typer.Option(
262
+ False, "--staging", help="Use staging API (staging.api.fluxloop.ai)"
263
+ ),
255
264
  ):
256
265
  """
257
266
  Generate scenario using Alignment Agent from user intent.
258
267
  """
259
- api_url = resolve_api_url(api_url)
268
+ api_url = resolve_api_url(api_url, staging=staging)
260
269
 
261
270
  # Build payload
262
271
  payload: Dict[str, Any] = {}
@@ -312,13 +321,16 @@ def list_scenarios(
312
321
  api_url: Optional[str] = typer.Option(
313
322
  None, "--api-url", help="FluxLoop API base URL"
314
323
  ),
324
+ staging: bool = typer.Option(
325
+ False, "--staging", help="Use staging API (staging.api.fluxloop.ai)"
326
+ ),
315
327
  ):
316
328
  """
317
329
  List all scenarios for a project.
318
330
 
319
331
  Uses current project from context if --project-id is not specified.
320
332
  """
321
- api_url = resolve_api_url(api_url)
333
+ api_url = resolve_api_url(api_url, staging=staging)
322
334
 
323
335
  # Use context if no project_id specified
324
336
  if not project_id:
@@ -397,6 +409,9 @@ def select(
397
409
  api_url: Optional[str] = typer.Option(
398
410
  None, "--api-url", help="FluxLoop API base URL"
399
411
  ),
412
+ staging: bool = typer.Option(
413
+ False, "--staging", help="Use staging API (staging.api.fluxloop.ai)"
414
+ ),
400
415
  ):
401
416
  """
402
417
  Select a scenario as the current working scenario.
@@ -404,7 +419,7 @@ def select(
404
419
  Use --local-path to specify which local folder to link.
405
420
  If not specified, tries to find existing folders automatically.
406
421
  """
407
- api_url = resolve_api_url(api_url)
422
+ api_url = resolve_api_url(api_url, staging=staging)
408
423
 
409
424
  try:
410
425
  # Fetch scenario details to get the name
@@ -452,11 +467,14 @@ def show(
452
467
  api_url: Optional[str] = typer.Option(
453
468
  None, "--api-url", help="FluxLoop API base URL"
454
469
  ),
470
+ staging: bool = typer.Option(
471
+ False, "--staging", help="Use staging API (staging.api.fluxloop.ai)"
472
+ ),
455
473
  ):
456
474
  """
457
475
  Show scenario details.
458
476
  """
459
- api_url = resolve_api_url(api_url)
477
+ api_url = resolve_api_url(api_url, staging=staging)
460
478
 
461
479
  try:
462
480
  client = create_authenticated_client(api_url, use_jwt=True)
@@ -506,11 +524,14 @@ def update(
506
524
  api_url: Optional[str] = typer.Option(
507
525
  None, "--api-url", help="FluxLoop API base URL"
508
526
  ),
527
+ staging: bool = typer.Option(
528
+ False, "--staging", help="Use staging API (staging.api.fluxloop.ai)"
529
+ ),
509
530
  ):
510
531
  """
511
532
  Update scenario fields.
512
533
  """
513
- api_url = resolve_api_url(api_url)
534
+ api_url = resolve_api_url(api_url, staging=staging)
514
535
 
515
536
  # Load payload from file
516
537
  payload = load_payload_file(file)
@@ -16,6 +16,7 @@ import typer
16
16
  import yaml
17
17
  from rich.console import Console
18
18
 
19
+ from ..api_utils import resolve_api_url
19
20
  from ..config_loader import load_experiment_config
20
21
  from ..constants import DEFAULT_CONFIG_PATH, FLUXLOOP_DIR_NAME, SCENARIOS_DIR_NAME
21
22
  from ..context_manager import get_current_web_project_id, get_current_scenario
@@ -98,15 +99,6 @@ def _load_env(scenario: Optional[str], base_dir: Optional[Path] = None) -> None:
98
99
  load_env_chain(source_dir, refresh_config=False)
99
100
 
100
101
 
101
- def _resolve_api_url(override: Optional[str]) -> str:
102
- url = (
103
- override
104
- or os.getenv("FLUXLOOP_SYNC_URL")
105
- or "https://api.fluxloop.ai"
106
- )
107
- return url.rstrip("/")
108
-
109
-
110
102
  def _resolve_api_key(override: Optional[str]) -> str:
111
103
  key = (
112
104
  override
@@ -165,7 +157,7 @@ def precreate_runs(
165
157
  Precreate runs via /api/sync/upload so streaming can reference existing runs.
166
158
  """
167
159
  _load_env(scenario, base_dir)
168
- api_url = _resolve_api_url(api_url)
160
+ api_url = resolve_api_url(api_url)
169
161
  api_key = _resolve_api_key(api_key)
170
162
  scenario_root = _resolve_scenario_dir(scenario, base_dir)
171
163
  sync_state = _read_sync_state(scenario_root)
@@ -229,7 +221,7 @@ def stream_turn(
229
221
  Stream a single turn to the sync API (best-effort).
230
222
  """
231
223
  try:
232
- api_url = _resolve_api_url(api_url)
224
+ api_url = resolve_api_url(api_url)
233
225
  api_key = _resolve_api_key(api_key)
234
226
  run_id = turn.get("run_id")
235
227
  turn_id = turn.get("turn_id")
@@ -528,6 +520,9 @@ def pull(
528
520
  ),
529
521
  scenario: Optional[str] = typer.Option(None, "--scenario", help="Scenario name (defaults to current context)"),
530
522
  api_url: Optional[str] = typer.Option(None, "--api-url", help="Sync API base URL"),
523
+ staging: bool = typer.Option(
524
+ False, "--staging", help="Use staging API (staging.api.fluxloop.ai)"
525
+ ),
531
526
  api_key: Optional[str] = typer.Option(None, "--api-key", help="Sync API key"),
532
527
  quiet: bool = typer.Option(False, "--quiet", help="Minimal output"),
533
528
  ):
@@ -543,7 +538,7 @@ def pull(
543
538
  console.print(f"[dim]Using scenario: {scenario}[/dim]")
544
539
 
545
540
  _load_env(scenario)
546
- api_url = _resolve_api_url(api_url)
541
+ api_url = resolve_api_url(api_url, staging=staging)
547
542
  api_key = _resolve_api_key(api_key)
548
543
 
549
544
  if not project_id:
@@ -644,6 +639,9 @@ def upload(
644
639
  ),
645
640
  scenario: Optional[str] = typer.Option(None, "--scenario", help="Scenario name (defaults to current context)"),
646
641
  api_url: Optional[str] = typer.Option(None, "--api-url", help="Sync API base URL"),
642
+ staging: bool = typer.Option(
643
+ False, "--staging", help="Use staging API (staging.api.fluxloop.ai)"
644
+ ),
647
645
  api_key: Optional[str] = typer.Option(None, "--api-key", help="Sync API key"),
648
646
  bundle_version_id: Optional[str] = typer.Option(
649
647
  None, "--bundle-version-id", help="Bundle version ID"
@@ -661,7 +659,7 @@ def upload(
661
659
  console.print(f"[dim]Using scenario: {scenario}[/dim]")
662
660
 
663
661
  _load_env(scenario)
664
- api_url = _resolve_api_url(api_url)
662
+ api_url = resolve_api_url(api_url, staging=staging)
665
663
  api_key = _resolve_api_key(api_key)
666
664
 
667
665
  scenario_root = _resolve_scenario_dir(scenario)
@@ -40,7 +40,7 @@ class WebProjectConnection:
40
40
  """
41
41
  project_id: str
42
42
  project_name: str
43
- api_url: str = "https://api.fluxloop.ai"
43
+ api_url: str = ""
44
44
  connected_at: Optional[str] = None
45
45
 
46
46
  def to_dict(self) -> dict:
@@ -56,7 +56,7 @@ class WebProjectConnection:
56
56
  return cls(
57
57
  project_id=data["project_id"],
58
58
  project_name=data["project_name"],
59
- api_url=data.get("api_url", "https://api.fluxloop.ai"),
59
+ api_url=data.get("api_url", ""),
60
60
  connected_at=data.get("connected_at"),
61
61
  )
62
62
 
@@ -336,7 +336,7 @@ def save_project_connection(connection: WebProjectConnection, base_dir: Optional
336
336
  def select_web_project(
337
337
  project_id: str,
338
338
  project_name: str,
339
- api_url: str = "https://api.fluxloop.ai",
339
+ api_url: str = "",
340
340
  base_dir: Optional[Path] = None
341
341
  ) -> WebProjectConnection:
342
342
  """Select a Web Project for this workspace."""
@@ -336,9 +336,8 @@ def create_env_file() -> str:
336
336
  return dedent(
337
337
  """
338
338
  # FluxLoop Configuration
339
- FLUXLOOP_SYNC_URL=https://api.fluxloop.ai
339
+ # API URL is managed via .fluxloop/project.json (set by 'fluxloop projects select')
340
340
  FLUXLOOP_SYNC_API_KEY=your-api-key-here
341
- # Legacy alias (still supported): FLUXLOOP_API_KEY
342
341
  FLUXLOOP_ENABLED=true
343
342
  FLUXLOOP_DEBUG=false
344
343
  FLUXLOOP_SAMPLE_RATE=1.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fluxloop-cli
3
- Version: 0.3.4
3
+ Version: 0.3.6
4
4
  Summary: FluxLoop CLI for running agent simulations
5
5
  Author-email: FluxLoop Team <team@fluxloop.dev>
6
6
  License: Apache-2.0
@@ -128,6 +128,21 @@ Options:
128
128
  After running the script, open the folder in VSCode and use `FluxLoop: Show Environment Info`
129
129
  or `FluxLoop: Run Doctor` to confirm the environment.
130
130
 
131
+ ## Full Reset Script
132
+
133
+ To remove FluxLoop-only local state (`.fluxloop*`, `~/.fluxloop`), remove
134
+ project deps via `uv remove` (when `pyproject.toml` exists), and uninstall
135
+ `fluxloop-cli` / `fluxloop` from pipx/uv tool/pip:
136
+
137
+ ```bash
138
+ bash reset_fluxloop_env.sh --dry-run
139
+ bash reset_fluxloop_env.sh --yes
140
+ ```
141
+
142
+ Options:
143
+
144
+ - `--project-root PATH` - clean a specific project root
145
+
131
146
  ## Runner Integration Patterns
132
147
 
133
148
  Configure how FluxLoop calls your code in `configs/simulation.yaml`:
@@ -156,4 +171,3 @@ To package the CLI:
156
171
  ```
157
172
  ./build.sh
158
173
  ```
159
-
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "fluxloop-cli"
7
- version = "0.3.4"
7
+ version = "0.3.6"
8
8
  description = "FluxLoop CLI for running agent simulations"
9
9
  authors = [
10
10
  {name = "FluxLoop Team", email = "team@fluxloop.dev"}
File without changes