fluxloop-cli 0.2.15__tar.gz → 0.2.17__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.
- {fluxloop_cli-0.2.15 → fluxloop_cli-0.2.17}/PKG-INFO +19 -1
- {fluxloop_cli-0.2.15 → fluxloop_cli-0.2.17}/README.md +18 -0
- {fluxloop_cli-0.2.15 → fluxloop_cli-0.2.17}/fluxloop_cli/__init__.py +1 -1
- {fluxloop_cli-0.2.15 → fluxloop_cli-0.2.17}/fluxloop_cli/commands/__init__.py +2 -2
- {fluxloop_cli-0.2.15 → fluxloop_cli-0.2.17}/fluxloop_cli/commands/config.py +20 -4
- fluxloop_cli-0.2.17/fluxloop_cli/commands/doctor.py +252 -0
- {fluxloop_cli-0.2.15 → fluxloop_cli-0.2.17}/fluxloop_cli/commands/init.py +17 -8
- {fluxloop_cli-0.2.15 → fluxloop_cli-0.2.17}/fluxloop_cli/main.py +2 -1
- {fluxloop_cli-0.2.15 → fluxloop_cli-0.2.17}/fluxloop_cli/runner.py +2 -3
- {fluxloop_cli-0.2.15 → fluxloop_cli-0.2.17}/fluxloop_cli/templates.py +3 -4
- {fluxloop_cli-0.2.15 → fluxloop_cli-0.2.17}/fluxloop_cli.egg-info/PKG-INFO +19 -1
- {fluxloop_cli-0.2.15 → fluxloop_cli-0.2.17}/fluxloop_cli.egg-info/SOURCES.txt +1 -0
- {fluxloop_cli-0.2.15 → fluxloop_cli-0.2.17}/pyproject.toml +1 -1
- {fluxloop_cli-0.2.15 → fluxloop_cli-0.2.17}/fluxloop_cli/arg_binder.py +0 -0
- {fluxloop_cli-0.2.15 → fluxloop_cli-0.2.17}/fluxloop_cli/commands/generate.py +0 -0
- {fluxloop_cli-0.2.15 → fluxloop_cli-0.2.17}/fluxloop_cli/commands/parse.py +0 -0
- {fluxloop_cli-0.2.15 → fluxloop_cli-0.2.17}/fluxloop_cli/commands/record.py +0 -0
- {fluxloop_cli-0.2.15 → fluxloop_cli-0.2.17}/fluxloop_cli/commands/run.py +0 -0
- {fluxloop_cli-0.2.15 → fluxloop_cli-0.2.17}/fluxloop_cli/commands/status.py +0 -0
- {fluxloop_cli-0.2.15 → fluxloop_cli-0.2.17}/fluxloop_cli/config_loader.py +0 -0
- {fluxloop_cli-0.2.15 → fluxloop_cli-0.2.17}/fluxloop_cli/config_schema.py +0 -0
- {fluxloop_cli-0.2.15 → fluxloop_cli-0.2.17}/fluxloop_cli/constants.py +0 -0
- {fluxloop_cli-0.2.15 → fluxloop_cli-0.2.17}/fluxloop_cli/input_generator.py +0 -0
- {fluxloop_cli-0.2.15 → fluxloop_cli-0.2.17}/fluxloop_cli/llm_generator.py +0 -0
- {fluxloop_cli-0.2.15 → fluxloop_cli-0.2.17}/fluxloop_cli/project_paths.py +0 -0
- {fluxloop_cli-0.2.15 → fluxloop_cli-0.2.17}/fluxloop_cli/target_loader.py +0 -0
- {fluxloop_cli-0.2.15 → fluxloop_cli-0.2.17}/fluxloop_cli/validators.py +0 -0
- {fluxloop_cli-0.2.15 → fluxloop_cli-0.2.17}/fluxloop_cli.egg-info/dependency_links.txt +0 -0
- {fluxloop_cli-0.2.15 → fluxloop_cli-0.2.17}/fluxloop_cli.egg-info/entry_points.txt +0 -0
- {fluxloop_cli-0.2.15 → fluxloop_cli-0.2.17}/fluxloop_cli.egg-info/requires.txt +0 -0
- {fluxloop_cli-0.2.15 → fluxloop_cli-0.2.17}/fluxloop_cli.egg-info/top_level.txt +0 -0
- {fluxloop_cli-0.2.15 → fluxloop_cli-0.2.17}/setup.cfg +0 -0
- {fluxloop_cli-0.2.15 → fluxloop_cli-0.2.17}/tests/test_arg_binder.py +0 -0
- {fluxloop_cli-0.2.15 → fluxloop_cli-0.2.17}/tests/test_config_command.py +0 -0
- {fluxloop_cli-0.2.15 → fluxloop_cli-0.2.17}/tests/test_input_generator.py +0 -0
- {fluxloop_cli-0.2.15 → fluxloop_cli-0.2.17}/tests/test_target_loader.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fluxloop-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.17
|
|
4
4
|
Summary: FluxLoop CLI for running agent simulations
|
|
5
5
|
Author-email: FluxLoop Team <team@fluxloop.dev>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -68,9 +68,27 @@ The legacy `setting.yaml` is still supported, but new projects created with
|
|
|
68
68
|
- `fluxloop parse experiment` – convert experiment outputs into readable artifacts
|
|
69
69
|
- `fluxloop config set-llm` – update LLM provider/model in `configs/input.yaml`
|
|
70
70
|
- `fluxloop record enable|disable|status` – toggle recording mode across `.env` and simulation config
|
|
71
|
+
- `fluxloop doctor` – summarize Python, FluxLoop CLI/MCP, and MCP index state for the active environment
|
|
71
72
|
|
|
72
73
|
Run `fluxloop --help` or `fluxloop <command> --help` for more detail.
|
|
73
74
|
|
|
75
|
+
## Quick Setup Script
|
|
76
|
+
|
|
77
|
+
To prepare a fresh checkout (create `.venv`, install dependencies, and run diagnostics):
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
bash scripts/setup_fluxloop_env.sh --target-source-root path/to/your/source
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Options:
|
|
84
|
+
|
|
85
|
+
- `--python PATH` – choose a specific interpreter (default `python3`)
|
|
86
|
+
- `--target-source-root PATH` – pre-populate VSCode `fluxloop.targetSourceRoot`
|
|
87
|
+
- `--skip-doctor` – skip the final `fluxloop doctor` check
|
|
88
|
+
|
|
89
|
+
After running the script, open the folder in VSCode and use `FluxLoop: Show Environment Info`
|
|
90
|
+
or `FluxLoop: Run Doctor` to confirm the environment.
|
|
91
|
+
|
|
74
92
|
## Runner Integration Patterns
|
|
75
93
|
|
|
76
94
|
Configure how FluxLoop calls your code in `configs/simulation.yaml`:
|
|
@@ -28,9 +28,27 @@ The legacy `setting.yaml` is still supported, but new projects created with
|
|
|
28
28
|
- `fluxloop parse experiment` – convert experiment outputs into readable artifacts
|
|
29
29
|
- `fluxloop config set-llm` – update LLM provider/model in `configs/input.yaml`
|
|
30
30
|
- `fluxloop record enable|disable|status` – toggle recording mode across `.env` and simulation config
|
|
31
|
+
- `fluxloop doctor` – summarize Python, FluxLoop CLI/MCP, and MCP index state for the active environment
|
|
31
32
|
|
|
32
33
|
Run `fluxloop --help` or `fluxloop <command> --help` for more detail.
|
|
33
34
|
|
|
35
|
+
## Quick Setup Script
|
|
36
|
+
|
|
37
|
+
To prepare a fresh checkout (create `.venv`, install dependencies, and run diagnostics):
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
bash scripts/setup_fluxloop_env.sh --target-source-root path/to/your/source
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Options:
|
|
44
|
+
|
|
45
|
+
- `--python PATH` – choose a specific interpreter (default `python3`)
|
|
46
|
+
- `--target-source-root PATH` – pre-populate VSCode `fluxloop.targetSourceRoot`
|
|
47
|
+
- `--skip-doctor` – skip the final `fluxloop doctor` check
|
|
48
|
+
|
|
49
|
+
After running the script, open the folder in VSCode and use `FluxLoop: Show Environment Info`
|
|
50
|
+
or `FluxLoop: Run Doctor` to confirm the environment.
|
|
51
|
+
|
|
34
52
|
## Runner Integration Patterns
|
|
35
53
|
|
|
36
54
|
Configure how FluxLoop calls your code in `configs/simulation.yaml`:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""CLI commands."""
|
|
2
2
|
|
|
3
|
-
from . import config, generate, init, parse, run, status, record
|
|
3
|
+
from . import config, generate, init, parse, run, status, record, doctor
|
|
4
4
|
|
|
5
|
-
__all__ = ["config", "generate", "init", "parse", "run", "status", "record"]
|
|
5
|
+
__all__ = ["config", "generate", "init", "parse", "run", "status", "record", "doctor"]
|
|
@@ -3,6 +3,7 @@ Config command for managing configuration.
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import os
|
|
6
|
+
import fluxloop
|
|
6
7
|
from pathlib import Path
|
|
7
8
|
from typing import Dict, Optional
|
|
8
9
|
|
|
@@ -174,6 +175,22 @@ def env(
|
|
|
174
175
|
"""
|
|
175
176
|
Show environment variables used by FluxLoop.
|
|
176
177
|
"""
|
|
178
|
+
# Load environment files so values reflect merged root -> project overrides
|
|
179
|
+
try:
|
|
180
|
+
from ..project_paths import resolve_root_dir, resolve_project_dir
|
|
181
|
+
loaded_paths = []
|
|
182
|
+
root_env = resolve_root_dir(root) / ".env"
|
|
183
|
+
if root_env.exists():
|
|
184
|
+
fluxloop.load_env(root_env, override=True, refresh_config=False)
|
|
185
|
+
loaded_paths.append(str(root_env))
|
|
186
|
+
if project:
|
|
187
|
+
project_env = resolve_project_dir(project, root) / ".env"
|
|
188
|
+
if project_env.exists():
|
|
189
|
+
fluxloop.load_env(project_env, override=True, refresh_config=False)
|
|
190
|
+
loaded_paths.append(str(project_env))
|
|
191
|
+
except Exception:
|
|
192
|
+
loaded_paths = []
|
|
193
|
+
|
|
177
194
|
env_vars = [
|
|
178
195
|
("FLUXLOOP_COLLECTOR_URL", "Collector service URL", "http://localhost:8000"),
|
|
179
196
|
("FLUXLOOP_API_KEY", "API key for authentication", None),
|
|
@@ -214,10 +231,9 @@ def env(
|
|
|
214
231
|
|
|
215
232
|
console.print(table)
|
|
216
233
|
|
|
217
|
-
#
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
console.print(f"\n[dim]Loading from:[/dim] {env_file}")
|
|
234
|
+
# Show loaded env sources (root first, then project)
|
|
235
|
+
if loaded_paths:
|
|
236
|
+
console.print("\n[dim]Loaded from:[/dim] " + ", ".join(loaded_paths))
|
|
221
237
|
else:
|
|
222
238
|
console.print("\n[yellow]No .env file found[/yellow]")
|
|
223
239
|
console.print("Create one with: [cyan]fluxloop init project[/cyan]")
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Doctor command for diagnosing FluxLoop environment issues.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
import os
|
|
9
|
+
import platform
|
|
10
|
+
import subprocess
|
|
11
|
+
import sys
|
|
12
|
+
from dataclasses import dataclass, asdict
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Any, Dict, Optional
|
|
15
|
+
|
|
16
|
+
import typer
|
|
17
|
+
from rich.console import Console
|
|
18
|
+
from rich.padding import Padding
|
|
19
|
+
from rich.panel import Panel
|
|
20
|
+
from rich.table import Table
|
|
21
|
+
|
|
22
|
+
from ..project_paths import resolve_config_directory, resolve_config_path
|
|
23
|
+
from ..constants import DEFAULT_CONFIG_PATH, DEFAULT_ROOT_DIR_NAME
|
|
24
|
+
|
|
25
|
+
app = typer.Typer(
|
|
26
|
+
help="Diagnose FluxLoop CLI, MCP and environment setup.",
|
|
27
|
+
invoke_without_command=True,
|
|
28
|
+
no_args_is_help=False,
|
|
29
|
+
)
|
|
30
|
+
console = Console()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass
|
|
34
|
+
class CommandResult:
|
|
35
|
+
success: bool
|
|
36
|
+
path: Optional[str]
|
|
37
|
+
output: Optional[str]
|
|
38
|
+
error: Optional[str]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _which(executable: str) -> Optional[str]:
|
|
42
|
+
from shutil import which
|
|
43
|
+
|
|
44
|
+
return which(executable)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _run_command(command: str, *args: str) -> CommandResult:
|
|
48
|
+
candidate = _which(command)
|
|
49
|
+
if not candidate:
|
|
50
|
+
return CommandResult(False, None, None, f"{command} not found on PATH")
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
completed = subprocess.run(
|
|
54
|
+
[candidate, *args],
|
|
55
|
+
capture_output=True,
|
|
56
|
+
text=True,
|
|
57
|
+
check=True,
|
|
58
|
+
)
|
|
59
|
+
return CommandResult(True, candidate, completed.stdout.strip(), completed.stderr.strip() or None)
|
|
60
|
+
except subprocess.CalledProcessError as exc:
|
|
61
|
+
return CommandResult(
|
|
62
|
+
False,
|
|
63
|
+
candidate,
|
|
64
|
+
exc.stdout.strip() or None,
|
|
65
|
+
exc.stderr.strip() or exc.stdout.strip() or str(exc),
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _detect_virtual_environment() -> Dict[str, Any]:
|
|
70
|
+
env_vars = {
|
|
71
|
+
"VIRTUAL_ENV": os.environ.get("VIRTUAL_ENV"),
|
|
72
|
+
"CONDA_PREFIX": os.environ.get("CONDA_PREFIX"),
|
|
73
|
+
"UV_PROJECT_ENV": os.environ.get("UV_PROJECT_ENV"),
|
|
74
|
+
}
|
|
75
|
+
virtualized = sys.prefix != getattr(sys, "base_prefix", sys.prefix)
|
|
76
|
+
return {
|
|
77
|
+
"python_executable": sys.executable,
|
|
78
|
+
"python_version": platform.python_version(),
|
|
79
|
+
"platform": platform.platform(),
|
|
80
|
+
"virtual_env": virtualized,
|
|
81
|
+
"virtual_env_path": (
|
|
82
|
+
env_vars["VIRTUAL_ENV"]
|
|
83
|
+
or env_vars["CONDA_PREFIX"]
|
|
84
|
+
or (Path(sys.prefix).as_posix() if virtualized else None)
|
|
85
|
+
),
|
|
86
|
+
"environment_variables": {k: v for k, v in env_vars.items() if v},
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _check_index_directory(index_dir: Path) -> Dict[str, Any]:
|
|
91
|
+
exists = index_dir.exists()
|
|
92
|
+
chunks_file = index_dir / "chunks.jsonl"
|
|
93
|
+
chunks_exists = chunks_file.exists()
|
|
94
|
+
chunks_size = chunks_file.stat().st_size if chunks_exists else 0
|
|
95
|
+
return {
|
|
96
|
+
"exists": exists,
|
|
97
|
+
"path": index_dir.as_posix(),
|
|
98
|
+
"chunks_exists": chunks_exists,
|
|
99
|
+
"chunks_size": chunks_size,
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _resolve_project_info(root: Path, project: Optional[str]) -> Dict[str, Any]:
|
|
104
|
+
config_path = resolve_config_path(DEFAULT_CONFIG_PATH, project, root)
|
|
105
|
+
config_dir = resolve_config_directory(project, root)
|
|
106
|
+
return {
|
|
107
|
+
"root": str(root.resolve()),
|
|
108
|
+
"config_directory": str(config_dir.resolve()),
|
|
109
|
+
"project_yaml": str(config_path.resolve()),
|
|
110
|
+
"project_yaml_exists": config_path.exists(),
|
|
111
|
+
"config_directory_exists": config_dir.exists(),
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _run_diagnostics(
|
|
116
|
+
project: Optional[str],
|
|
117
|
+
root: Path,
|
|
118
|
+
index_dir: Optional[Path],
|
|
119
|
+
output_json: bool,
|
|
120
|
+
) -> None:
|
|
121
|
+
console.print(Panel.fit("FluxLoop Environment Doctor", border_style="blue"))
|
|
122
|
+
|
|
123
|
+
virtual_info = _detect_virtual_environment()
|
|
124
|
+
fluxloop_result = _run_command("fluxloop", "--version")
|
|
125
|
+
mcp_result = _run_command("fluxloop-mcp", "--help")
|
|
126
|
+
python_result = _run_command(sys.executable, "--version")
|
|
127
|
+
|
|
128
|
+
resolved_root = root
|
|
129
|
+
project_info = _resolve_project_info(resolved_root, project)
|
|
130
|
+
resolved_index_dir = (
|
|
131
|
+
index_dir
|
|
132
|
+
if index_dir is not None
|
|
133
|
+
else Path.home() / ".fluxloop" / "mcp" / "index" / "dev"
|
|
134
|
+
)
|
|
135
|
+
index_info = _check_index_directory(resolved_index_dir)
|
|
136
|
+
|
|
137
|
+
diagnostics = {
|
|
138
|
+
"python": {
|
|
139
|
+
"executable": sys.executable,
|
|
140
|
+
"version": virtual_info["python_version"],
|
|
141
|
+
"platform": virtual_info["platform"],
|
|
142
|
+
"command_output": python_result.output,
|
|
143
|
+
},
|
|
144
|
+
"virtual_environment": virtual_info,
|
|
145
|
+
"fluxloop_cli": asdict(fluxloop_result),
|
|
146
|
+
"fluxloop_mcp": asdict(mcp_result),
|
|
147
|
+
"project": project_info,
|
|
148
|
+
"mcp_index": index_info,
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if output_json:
|
|
152
|
+
typer.echo(json.dumps(diagnostics, indent=2, default=str))
|
|
153
|
+
raise typer.Exit()
|
|
154
|
+
|
|
155
|
+
items_panel = Table(show_header=False, box=None)
|
|
156
|
+
items_panel.add_column("Component", style="cyan", no_wrap=True)
|
|
157
|
+
items_panel.add_column("Status")
|
|
158
|
+
items_panel.add_column("Details", style="dim")
|
|
159
|
+
|
|
160
|
+
items_panel.add_row(
|
|
161
|
+
"Python",
|
|
162
|
+
"[green]✓[/green]" if python_result.success else "[red]✗[/red]",
|
|
163
|
+
f"{virtual_info['python_version']} ({sys.executable})",
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
items_panel.add_row(
|
|
167
|
+
"Virtual Env",
|
|
168
|
+
"[green]✓[/green]" if virtual_info["virtual_env"] else "[yellow]–[/yellow]",
|
|
169
|
+
virtual_info["virtual_env_path"] or "Global interpreter",
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
items_panel.add_row(
|
|
173
|
+
"FluxLoop CLI",
|
|
174
|
+
"[green]✓[/green]" if fluxloop_result.success else "[red]✗[/red]",
|
|
175
|
+
fluxloop_result.path or fluxloop_result.error or "Not found",
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
items_panel.add_row(
|
|
179
|
+
"FluxLoop MCP",
|
|
180
|
+
"[green]✓[/green]" if mcp_result.success else "[red]✗[/red]",
|
|
181
|
+
mcp_result.path or mcp_result.error or "Not found",
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
index_status = "[green]✓[/green]" if index_info["exists"] else "[yellow]–[/yellow]"
|
|
185
|
+
index_details = index_info["path"]
|
|
186
|
+
if index_info["exists"]:
|
|
187
|
+
chunk_detail = (
|
|
188
|
+
f"chunks.jsonl ({index_info['chunks_size']} bytes)"
|
|
189
|
+
if index_info["chunks_exists"]
|
|
190
|
+
else "missing chunks.jsonl"
|
|
191
|
+
)
|
|
192
|
+
index_details = f"{index_details} • {chunk_detail}"
|
|
193
|
+
|
|
194
|
+
items_panel.add_row("MCP Index", index_status, index_details)
|
|
195
|
+
|
|
196
|
+
config_status = (
|
|
197
|
+
"[green]✓[/green]" if project_info["project_yaml_exists"] else "[yellow]–[/yellow]"
|
|
198
|
+
)
|
|
199
|
+
config_details = (
|
|
200
|
+
f"{project_info['project_yaml']} (project.yaml)"
|
|
201
|
+
if project_info["project_yaml_exists"]
|
|
202
|
+
else "Run: fluxloop init project"
|
|
203
|
+
)
|
|
204
|
+
items_panel.add_row("Project Config", config_status, config_details)
|
|
205
|
+
|
|
206
|
+
console.print(Padding(items_panel, (1, 0, 1, 0)))
|
|
207
|
+
|
|
208
|
+
if fluxloop_result.error or mcp_result.error:
|
|
209
|
+
console.print("[bold red]Errors[/bold red]")
|
|
210
|
+
if fluxloop_result.error:
|
|
211
|
+
console.print(f"• FluxLoop CLI: {fluxloop_result.error}")
|
|
212
|
+
if mcp_result.error:
|
|
213
|
+
console.print(f"• fluxloop-mcp: {mcp_result.error}")
|
|
214
|
+
console.print()
|
|
215
|
+
|
|
216
|
+
if diagnostics["fluxloop_cli"]["output"]:
|
|
217
|
+
console.print(Panel(diagnostics["fluxloop_cli"]["output"], title="fluxloop --version", border_style="green"))
|
|
218
|
+
if diagnostics["fluxloop_mcp"]["output"]:
|
|
219
|
+
console.print(Panel(diagnostics["fluxloop_mcp"]["output"], title="fluxloop-mcp --help", border_style="green"))
|
|
220
|
+
|
|
221
|
+
console.print(Padding(Panel.fit("Doctor completed", border_style="green"), (1, 0, 0, 0)))
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
@app.callback()
|
|
225
|
+
def doctor_callback(
|
|
226
|
+
ctx: typer.Context,
|
|
227
|
+
project: Optional[str] = typer.Option(
|
|
228
|
+
None, "--project", "-p", help="Project name under the FluxLoop root directory."
|
|
229
|
+
),
|
|
230
|
+
root: Path = typer.Option(
|
|
231
|
+
Path(DEFAULT_ROOT_DIR_NAME),
|
|
232
|
+
"--root",
|
|
233
|
+
help="FluxLoop root directory (defaults to ./fluxloop).",
|
|
234
|
+
),
|
|
235
|
+
index_dir: Optional[Path] = typer.Option(
|
|
236
|
+
None,
|
|
237
|
+
"--index-dir",
|
|
238
|
+
help="Override FluxLoop MCP index directory.",
|
|
239
|
+
),
|
|
240
|
+
output_json: bool = typer.Option(
|
|
241
|
+
False,
|
|
242
|
+
"--json",
|
|
243
|
+
help="Output diagnostic information as JSON.",
|
|
244
|
+
),
|
|
245
|
+
) -> None:
|
|
246
|
+
"""
|
|
247
|
+
Diagnose the FluxLoop CLI, MCP installation, virtual environment, and project configuration.
|
|
248
|
+
"""
|
|
249
|
+
if ctx.invoked_subcommand:
|
|
250
|
+
return
|
|
251
|
+
_run_diagnostics(project, root, index_dir, output_json)
|
|
252
|
+
|
|
@@ -137,18 +137,27 @@ def project(
|
|
|
137
137
|
section_path = section_paths[key]
|
|
138
138
|
section_path.write_text(content)
|
|
139
139
|
|
|
140
|
-
#
|
|
140
|
+
# Create project .env (unified)
|
|
141
141
|
root_env_file = root_dir / ".env"
|
|
142
|
-
|
|
143
|
-
console.print("🔐 Creating root environment template...")
|
|
144
|
-
root_env_file.write_text(create_env_file())
|
|
145
|
-
|
|
146
|
-
console.print("🔐 Creating project .env template...")
|
|
142
|
+
console.print("🔐 Creating project .env...")
|
|
147
143
|
recordings_dir = project_path / "recordings"
|
|
148
144
|
recordings_dir.mkdir(exist_ok=True)
|
|
149
145
|
|
|
150
|
-
|
|
151
|
-
|
|
146
|
+
# If a root .env exists, seed project .env with its contents so project can override locally
|
|
147
|
+
if root_env_file.exists():
|
|
148
|
+
try:
|
|
149
|
+
root_contents = root_env_file.read_text()
|
|
150
|
+
except Exception:
|
|
151
|
+
root_contents = ""
|
|
152
|
+
merged = (
|
|
153
|
+
"# Project .env (seeded from FluxLoop root .env)\n"
|
|
154
|
+
+ (root_contents if root_contents.endswith("\n") else root_contents + "\n")
|
|
155
|
+
+ "\n# Project-specific overrides (take precedence)\n"
|
|
156
|
+
)
|
|
157
|
+
env_file.write_text(merged)
|
|
158
|
+
console.print("[dim]Seeded from root .env (project overrides take precedence).[/dim]")
|
|
159
|
+
else:
|
|
160
|
+
env_file.write_text(create_env_file())
|
|
152
161
|
|
|
153
162
|
# Update .gitignore
|
|
154
163
|
if not gitignore_file.exists():
|
|
@@ -11,7 +11,7 @@ from rich.console import Console
|
|
|
11
11
|
from rich.panel import Panel
|
|
12
12
|
|
|
13
13
|
from . import __version__
|
|
14
|
-
from .commands import config, generate, init, parse, run, status, record
|
|
14
|
+
from .commands import config, doctor, generate, init, parse, run, status, record
|
|
15
15
|
|
|
16
16
|
# Create the main Typer app
|
|
17
17
|
app = typer.Typer(
|
|
@@ -32,6 +32,7 @@ app.add_typer(config.app, name="config", help="Manage configuration")
|
|
|
32
32
|
app.add_typer(generate.app, name="generate", help="Generate input datasets")
|
|
33
33
|
app.add_typer(parse.app, name="parse", help="Parse experiments into readable files")
|
|
34
34
|
app.add_typer(record.app, name="record", help="Manage recording mode and settings")
|
|
35
|
+
app.add_typer(doctor.app, name="doctor", help="Diagnose CLI and MCP environment issues")
|
|
35
36
|
|
|
36
37
|
|
|
37
38
|
def version_callback(value: bool):
|
|
@@ -99,11 +99,12 @@ class ExperimentRunner:
|
|
|
99
99
|
source_dir = self.config.get_source_dir()
|
|
100
100
|
env_candidates: List[Path] = []
|
|
101
101
|
|
|
102
|
+
# Load parent (root) .env first, then project .env so project overrides take precedence
|
|
102
103
|
if source_dir:
|
|
103
|
-
env_candidates.append(source_dir / ".env")
|
|
104
104
|
parent = source_dir.parent
|
|
105
105
|
if parent != source_dir:
|
|
106
106
|
env_candidates.append(parent / ".env")
|
|
107
|
+
env_candidates.append(source_dir / ".env")
|
|
107
108
|
|
|
108
109
|
for candidate in env_candidates:
|
|
109
110
|
if candidate.exists():
|
|
@@ -111,8 +112,6 @@ class ExperimentRunner:
|
|
|
111
112
|
fluxloop.load_env(candidate, override=True, refresh_config=True)
|
|
112
113
|
except Exception:
|
|
113
114
|
console.log(f"[yellow]Warning:[/yellow] Failed to load environment from {candidate}")
|
|
114
|
-
else:
|
|
115
|
-
break
|
|
116
115
|
|
|
117
116
|
env_vars = getattr(self.config.runner, "environment_vars", {}) or {}
|
|
118
117
|
for key, value in env_vars.items():
|
|
@@ -15,6 +15,9 @@ def create_project_config(project_name: str) -> str:
|
|
|
15
15
|
description: AI agent simulation project
|
|
16
16
|
version: 1.0.0
|
|
17
17
|
|
|
18
|
+
# FluxLoop VSCode extension will prompt to set this path
|
|
19
|
+
source_root: ""
|
|
20
|
+
|
|
18
21
|
collector_url: null
|
|
19
22
|
collector_api_key: null
|
|
20
23
|
|
|
@@ -63,10 +66,6 @@ def create_input_config() -> str:
|
|
|
63
66
|
base_inputs:
|
|
64
67
|
- input: "How do I get started?"
|
|
65
68
|
expected_intent: help
|
|
66
|
-
- input: "What can you do?"
|
|
67
|
-
expected_intent: capabilities
|
|
68
|
-
- input: "Show me an example"
|
|
69
|
-
expected_intent: demo
|
|
70
69
|
|
|
71
70
|
variation_strategies:
|
|
72
71
|
- rephrase
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fluxloop-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.17
|
|
4
4
|
Summary: FluxLoop CLI for running agent simulations
|
|
5
5
|
Author-email: FluxLoop Team <team@fluxloop.dev>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -68,9 +68,27 @@ The legacy `setting.yaml` is still supported, but new projects created with
|
|
|
68
68
|
- `fluxloop parse experiment` – convert experiment outputs into readable artifacts
|
|
69
69
|
- `fluxloop config set-llm` – update LLM provider/model in `configs/input.yaml`
|
|
70
70
|
- `fluxloop record enable|disable|status` – toggle recording mode across `.env` and simulation config
|
|
71
|
+
- `fluxloop doctor` – summarize Python, FluxLoop CLI/MCP, and MCP index state for the active environment
|
|
71
72
|
|
|
72
73
|
Run `fluxloop --help` or `fluxloop <command> --help` for more detail.
|
|
73
74
|
|
|
75
|
+
## Quick Setup Script
|
|
76
|
+
|
|
77
|
+
To prepare a fresh checkout (create `.venv`, install dependencies, and run diagnostics):
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
bash scripts/setup_fluxloop_env.sh --target-source-root path/to/your/source
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Options:
|
|
84
|
+
|
|
85
|
+
- `--python PATH` – choose a specific interpreter (default `python3`)
|
|
86
|
+
- `--target-source-root PATH` – pre-populate VSCode `fluxloop.targetSourceRoot`
|
|
87
|
+
- `--skip-doctor` – skip the final `fluxloop doctor` check
|
|
88
|
+
|
|
89
|
+
After running the script, open the folder in VSCode and use `FluxLoop: Show Environment Info`
|
|
90
|
+
or `FluxLoop: Run Doctor` to confirm the environment.
|
|
91
|
+
|
|
74
92
|
## Runner Integration Patterns
|
|
75
93
|
|
|
76
94
|
Configure how FluxLoop calls your code in `configs/simulation.yaml`:
|
|
@@ -21,6 +21,7 @@ fluxloop_cli.egg-info/requires.txt
|
|
|
21
21
|
fluxloop_cli.egg-info/top_level.txt
|
|
22
22
|
fluxloop_cli/commands/__init__.py
|
|
23
23
|
fluxloop_cli/commands/config.py
|
|
24
|
+
fluxloop_cli/commands/doctor.py
|
|
24
25
|
fluxloop_cli/commands/generate.py
|
|
25
26
|
fluxloop_cli/commands/init.py
|
|
26
27
|
fluxloop_cli/commands/parse.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|