agent-brain-cli 10.0.4__tar.gz → 10.0.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.
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/PKG-INFO +2 -2
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/agent_brain_cli/__init__.py +1 -1
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/agent_brain_cli/commands/doctor.py +38 -2
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/agent_brain_cli/commands/jobs.py +31 -7
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/agent_brain_cli/config.py +23 -10
- agent_brain_cli-10.0.6/agent_brain_cli/diagnostics.py +737 -0
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/pyproject.toml +2 -2
- agent_brain_cli-10.0.4/agent_brain_cli/diagnostics.py +0 -384
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/README.md +0 -0
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/agent_brain_cli/cli.py +0 -0
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/agent_brain_cli/client/__init__.py +0 -0
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/agent_brain_cli/client/api_client.py +0 -0
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/agent_brain_cli/commands/__init__.py +0 -0
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/agent_brain_cli/commands/cache.py +0 -0
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/agent_brain_cli/commands/config.py +0 -0
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/agent_brain_cli/commands/folders.py +0 -0
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/agent_brain_cli/commands/index.py +0 -0
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/agent_brain_cli/commands/init.py +0 -0
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/agent_brain_cli/commands/inject.py +0 -0
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/agent_brain_cli/commands/install_agent.py +0 -0
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/agent_brain_cli/commands/list_cmd.py +0 -0
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/agent_brain_cli/commands/query.py +0 -0
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/agent_brain_cli/commands/reset.py +0 -0
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/agent_brain_cli/commands/start.py +0 -0
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/agent_brain_cli/commands/status.py +0 -0
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/agent_brain_cli/commands/stop.py +0 -0
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/agent_brain_cli/commands/types.py +0 -0
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/agent_brain_cli/commands/uninstall.py +0 -0
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/agent_brain_cli/config_migrate.py +0 -0
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/agent_brain_cli/config_schema.py +0 -0
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/agent_brain_cli/migration.py +0 -0
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/agent_brain_cli/runtime/__init__.py +0 -0
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/agent_brain_cli/runtime/claude_converter.py +0 -0
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/agent_brain_cli/runtime/codex_converter.py +0 -0
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/agent_brain_cli/runtime/converter_base.py +0 -0
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/agent_brain_cli/runtime/gemini_converter.py +0 -0
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/agent_brain_cli/runtime/opencode_converter.py +0 -0
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/agent_brain_cli/runtime/parser.py +0 -0
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/agent_brain_cli/runtime/skill_runtime_converter.py +0 -0
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/agent_brain_cli/runtime/tool_maps.py +0 -0
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/agent_brain_cli/runtime/types.py +0 -0
- {agent_brain_cli-10.0.4 → agent_brain_cli-10.0.6}/agent_brain_cli/xdg_paths.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: agent-brain-cli
|
|
3
|
-
Version: 10.0.
|
|
3
|
+
Version: 10.0.6
|
|
4
4
|
Summary: Agent Brain CLI - Command-line interface for managing AI agent memory and knowledge retrieval
|
|
5
5
|
Home-page: https://github.com/SpillwaveSolutions/agent-brain
|
|
6
6
|
License: MIT
|
|
@@ -15,7 +15,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.10
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.11
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
-
Requires-Dist: agent-brain-rag (>=10.0.
|
|
18
|
+
Requires-Dist: agent-brain-rag (>=10.0.6,<11.0.0)
|
|
19
19
|
Requires-Dist: click (>=8.1.0,<9.0.0)
|
|
20
20
|
Requires-Dist: httpx (>=0.28.0,<0.29.0)
|
|
21
21
|
Requires-Dist: pydantic (>=2.10.0,<3.0.0)
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import json
|
|
6
|
+
|
|
5
7
|
import click
|
|
6
8
|
from rich.console import Console
|
|
7
9
|
from rich.panel import Panel
|
|
@@ -11,6 +13,7 @@ from agent_brain_cli.diagnostics import (
|
|
|
11
13
|
SEVERITY_FAIL,
|
|
12
14
|
SEVERITY_OK,
|
|
13
15
|
SEVERITY_WARN,
|
|
16
|
+
apply_safe_fixes,
|
|
14
17
|
report_to_json,
|
|
15
18
|
run_doctor,
|
|
16
19
|
)
|
|
@@ -33,18 +36,40 @@ _STATUS_STYLE = {
|
|
|
33
36
|
help="Server URL to probe (default: resolved from runtime.json or config).",
|
|
34
37
|
)
|
|
35
38
|
@click.option("--json", "json_output", is_flag=True, help="Emit machine-readable JSON.")
|
|
36
|
-
|
|
39
|
+
@click.option(
|
|
40
|
+
"--fix",
|
|
41
|
+
"apply_fixes",
|
|
42
|
+
is_flag=True,
|
|
43
|
+
help=(
|
|
44
|
+
"Apply safe, idempotent, offline fixes (add .agent-brain/ to .gitignore, "
|
|
45
|
+
"create state dir + stub config.json). Will not touch API keys, network, "
|
|
46
|
+
"or user code. Re-runs the report after fixing."
|
|
47
|
+
),
|
|
48
|
+
)
|
|
49
|
+
def doctor_command(url: str | None, json_output: bool, apply_fixes: bool) -> None:
|
|
37
50
|
"""Diagnose your Agent Brain setup.
|
|
38
51
|
|
|
39
52
|
Inspects Python version, project init state, provider config, required
|
|
40
53
|
API keys, optional dependencies, .gitignore hygiene, and whether the
|
|
41
54
|
server is reachable. Exits non-zero on any critical failure so it can
|
|
42
55
|
be used in scripts (``agent-brain doctor || agent-brain init``).
|
|
56
|
+
|
|
57
|
+
Pass ``--fix`` to auto-apply the safe subset of remediations and re-run.
|
|
43
58
|
"""
|
|
44
59
|
report = run_doctor(server_url_override=url)
|
|
45
60
|
|
|
61
|
+
fix_actions: list[str] = []
|
|
62
|
+
if apply_fixes:
|
|
63
|
+
fix_actions = apply_safe_fixes(report)
|
|
64
|
+
if fix_actions:
|
|
65
|
+
# Re-run so the printed report reflects the fixed state.
|
|
66
|
+
report = run_doctor(server_url_override=url)
|
|
67
|
+
|
|
46
68
|
if json_output:
|
|
47
|
-
|
|
69
|
+
payload = json.loads(report_to_json(report))
|
|
70
|
+
if apply_fixes:
|
|
71
|
+
payload["applied_fixes"] = fix_actions
|
|
72
|
+
click.echo(json.dumps(payload, indent=2))
|
|
48
73
|
raise SystemExit(report.exit_code)
|
|
49
74
|
|
|
50
75
|
header_color = "green" if report.exit_code == 0 else "red"
|
|
@@ -75,6 +100,17 @@ def doctor_command(url: str | None, json_output: bool) -> None:
|
|
|
75
100
|
|
|
76
101
|
console.print(table)
|
|
77
102
|
|
|
103
|
+
if apply_fixes:
|
|
104
|
+
if fix_actions:
|
|
105
|
+
console.print("\n[cyan]Applied safe fixes:[/]")
|
|
106
|
+
for action in fix_actions:
|
|
107
|
+
console.print(f" • {action}")
|
|
108
|
+
else:
|
|
109
|
+
console.print(
|
|
110
|
+
"\n[dim]No safe fixes applied (nothing actionable, or all "
|
|
111
|
+
"checks already passing).[/]"
|
|
112
|
+
)
|
|
113
|
+
|
|
78
114
|
if report.exit_code != 0:
|
|
79
115
|
console.print(
|
|
80
116
|
"\n[red]Doctor reported critical issues.[/] "
|
|
@@ -25,13 +25,32 @@ def _format_timestamp(ts: str | None) -> str:
|
|
|
25
25
|
return ts
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
def _format_progress(
|
|
29
|
-
|
|
28
|
+
def _format_progress(
|
|
29
|
+
progress: float | int | dict[str, Any] | None,
|
|
30
|
+
total: int | None,
|
|
31
|
+
) -> str:
|
|
32
|
+
"""Format progress for display.
|
|
33
|
+
|
|
34
|
+
Copes with both the legacy float shape and the structured ``JobProgress``
|
|
35
|
+
dict the server emits today (#150). Falls back to a string coercion for
|
|
36
|
+
unknown types so the CLI never raises.
|
|
37
|
+
"""
|
|
30
38
|
if progress is None:
|
|
31
39
|
return "-"
|
|
32
|
-
if
|
|
33
|
-
|
|
34
|
-
|
|
40
|
+
if isinstance(progress, dict):
|
|
41
|
+
pct = progress.get("percent_complete")
|
|
42
|
+
if isinstance(pct, (int, float)):
|
|
43
|
+
files_total = progress.get("files_total") or 0
|
|
44
|
+
files_done = progress.get("files_processed") or 0
|
|
45
|
+
if files_total:
|
|
46
|
+
return f"{pct:.1f}% ({files_done}/{files_total} files)"
|
|
47
|
+
return f"{pct:.1f}%"
|
|
48
|
+
return ", ".join(f"{k}={v}" for k, v in progress.items())
|
|
49
|
+
if isinstance(progress, (int, float)):
|
|
50
|
+
if total:
|
|
51
|
+
return f"{progress:.1f}% ({total} files)"
|
|
52
|
+
return f"{progress:.1f}%"
|
|
53
|
+
return str(progress)
|
|
35
54
|
|
|
36
55
|
|
|
37
56
|
def _get_status_style(status: str) -> str:
|
|
@@ -121,12 +140,17 @@ def _create_job_detail_panel(job: dict[str, Any]) -> Panel:
|
|
|
121
140
|
if (progress := job.get("progress_percent", job.get("progress"))) is not None:
|
|
122
141
|
total = job.get("total_files", 0)
|
|
123
142
|
processed = job.get("processed_files", 0)
|
|
124
|
-
|
|
143
|
+
# #150: server emits structured JobProgress dicts in addition to floats.
|
|
144
|
+
# Defer formatting to _format_progress so list-view and detail-view
|
|
145
|
+
# never diverge on type handling.
|
|
146
|
+
if isinstance(progress, (int, float)) and total:
|
|
125
147
|
lines.append(
|
|
126
148
|
f"[bold]Progress:[/] {progress:.1f}% ({processed}/{total} files)"
|
|
127
149
|
)
|
|
128
150
|
else:
|
|
129
|
-
lines.append(
|
|
151
|
+
lines.append(
|
|
152
|
+
f"[bold]Progress:[/] {_format_progress(progress, total or None)}"
|
|
153
|
+
)
|
|
130
154
|
|
|
131
155
|
if enqueued := job.get("enqueued_at", job.get("created_at")):
|
|
132
156
|
lines.append(f"[bold]Enqueued:[/] {_format_timestamp(enqueued)}")
|
|
@@ -254,6 +254,20 @@ def load_config(start_path: Path | None = None) -> AgentBrainConfig:
|
|
|
254
254
|
def resolve_project_root(start_path: Path | None = None) -> Path:
|
|
255
255
|
"""Find the project root by looking for markers.
|
|
256
256
|
|
|
257
|
+
Thin wrapper around :func:`resolve_project_root_with_strategy` that drops
|
|
258
|
+
the strategy label for callers that only need the path.
|
|
259
|
+
"""
|
|
260
|
+
return resolve_project_root_with_strategy(start_path)[0]
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def resolve_project_root_with_strategy(
|
|
264
|
+
start_path: Path | None = None,
|
|
265
|
+
) -> tuple[Path, str]:
|
|
266
|
+
"""Find the project root and report *which* rule matched.
|
|
267
|
+
|
|
268
|
+
Used by ``agent-brain doctor`` to explain why a given directory was
|
|
269
|
+
selected (issue #146).
|
|
270
|
+
|
|
257
271
|
Resolution order (first match wins):
|
|
258
272
|
1. Walk up from ``start_path`` looking for ``.agent-brain/`` — this lets a
|
|
259
273
|
sub-project inside a mono-repo keep its own state dir and not get
|
|
@@ -263,11 +277,10 @@ def resolve_project_root(start_path: Path | None = None) -> Path:
|
|
|
263
277
|
4. Walk up looking for ``.claude/`` or ``pyproject.toml``.
|
|
264
278
|
5. Fall back to ``start_path``.
|
|
265
279
|
|
|
266
|
-
Args:
|
|
267
|
-
start_path: Starting directory. Defaults to cwd.
|
|
268
|
-
|
|
269
280
|
Returns:
|
|
270
|
-
|
|
281
|
+
``(root, strategy)`` where ``strategy`` is one of
|
|
282
|
+
``"agent_brain_dir"``, ``"legacy_claude_dir"``, ``"git_root"``,
|
|
283
|
+
``"claude_dir"``, ``"pyproject"``, ``"cwd_fallback"``.
|
|
271
284
|
"""
|
|
272
285
|
import subprocess
|
|
273
286
|
|
|
@@ -277,9 +290,9 @@ def resolve_project_root(start_path: Path | None = None) -> Path:
|
|
|
277
290
|
current = start
|
|
278
291
|
while current != current.parent:
|
|
279
292
|
if (current / STATE_DIR_NAME).is_dir():
|
|
280
|
-
return current
|
|
293
|
+
return current, "agent_brain_dir"
|
|
281
294
|
if (current / LEGACY_STATE_DIR_NAME).is_dir():
|
|
282
|
-
return current
|
|
295
|
+
return current, "legacy_claude_dir"
|
|
283
296
|
current = current.parent
|
|
284
297
|
|
|
285
298
|
# 3. Git root next — useful when this is the first time the user runs init.
|
|
@@ -292,7 +305,7 @@ def resolve_project_root(start_path: Path | None = None) -> Path:
|
|
|
292
305
|
cwd=str(start),
|
|
293
306
|
)
|
|
294
307
|
if result.returncode == 0:
|
|
295
|
-
return Path(result.stdout.strip()).resolve()
|
|
308
|
+
return Path(result.stdout.strip()).resolve(), "git_root"
|
|
296
309
|
except (subprocess.TimeoutExpired, FileNotFoundError, OSError):
|
|
297
310
|
pass
|
|
298
311
|
|
|
@@ -300,12 +313,12 @@ def resolve_project_root(start_path: Path | None = None) -> Path:
|
|
|
300
313
|
current = start
|
|
301
314
|
while current != current.parent:
|
|
302
315
|
if (current / ".claude").is_dir():
|
|
303
|
-
return current
|
|
316
|
+
return current, "claude_dir"
|
|
304
317
|
if (current / "pyproject.toml").is_file():
|
|
305
|
-
return current
|
|
318
|
+
return current, "pyproject"
|
|
306
319
|
current = current.parent
|
|
307
320
|
|
|
308
|
-
return start
|
|
321
|
+
return start, "cwd_fallback"
|
|
309
322
|
|
|
310
323
|
|
|
311
324
|
# Backwards-compatible alias for any external callers.
|