cdx-manager 0.12.1 → 0.12.2

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.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # CDX Manager
2
2
 
3
- [![License](https://img.shields.io/badge/license-MIT-4C8BF5)](LICENSE) ![Version](https://img.shields.io/badge/version-v0.12.1-4C8BF5) ![Python](https://img.shields.io/badge/python-3.9%2B-3776AB?logo=python&logoColor=white)
3
+ [![License](https://img.shields.io/badge/license-MIT-4C8BF5)](LICENSE) ![Version](https://img.shields.io/badge/version-v0.12.2-4C8BF5) ![Python](https://img.shields.io/badge/python-3.9%2B-3776AB?logo=python&logoColor=white)
4
4
 
5
5
  **Run multiple Codex, Claude, Antigravity, and Ollama sessions from one terminal. Switch between accounts instantly.**
6
6
 
@@ -274,7 +274,7 @@ cdx disk profiles --candidates
274
274
 
275
275
  ### Next-Ready Notifications
276
276
 
277
- Use `cdx ready` when every useful assistant is cooling down and you want your terminal back. It is shorthand for `cdx notify --schedule --next-ready`: cdx picks the next known reset, registers a native OS notification, and exits immediately.
277
+ Use `cdx ready` when every useful assistant is cooling down and you want your terminal back. It is shorthand for `cdx notify --schedule --next-ready`: cdx picks the next known reset, registers a native OS notification, and exits immediately. If no future reset is known, it exits successfully and explains that no notification was scheduled.
278
278
 
279
279
  ```bash
280
280
  cdx ready
@@ -372,7 +372,7 @@ cdx history --summary --from 2026-05-01 --to 2026-05-28
372
372
  | `cdx reset <name> [--yes] [--json]` | Explicitly consume one available banked Codex rate-limit reset; confirmation is required unless `--yes` is supplied |
373
373
  | `cdx export <file> [--include-auth] [--sessions a,b] [--passphrase-env VAR\|--passphrase-stdin] [--force] [--json]` | Export sessions to a portable bundle; `--include-auth` encrypts auth data with a passphrase |
374
374
  | `cdx import <file> [--sessions a,b] [--passphrase-env VAR\|--passphrase-stdin] [--force] [--json]` | Import sessions from a bundle into the current `CDX_HOME` |
375
- | `cdx doctor [--json]` | Inspect CLI dependencies, provider CLI versions/capability hints, CDX_HOME permissions, missing state, orphan profiles, and pending quarantines |
375
+ | `cdx doctor [--severity OK|WARN|FAIL[,OK|WARN|FAIL...]] [--json]` | Inspect CLI dependencies, provider CLI versions/capability hints, CDX_HOME permissions, missing state, orphan profiles, and pending quarantines; optionally filter issue severities |
376
376
  | `cdx repair [--dry-run] [--force] [--json]` | Plan or apply safe repairs for missing state files, quarantines, and orphan profiles |
377
377
  | `cdx view [--json] [--lan] [--lan-rw] [--focus <ref>] [--read] [--port <port>] [--host <host>] [--refresh-interval <s>] [--tls] [--tls-cert <path>] [--tls-key <path>] [--open] [--no-open]` | Open the Logics browser/focus viewer by delegating to `logics-manager view`; all viewer flags are forwarded; JSON mode reports diagnostics without launching it |
378
378
  | `cdx update [--check] [--yes] [--json] [--version TAG]` | Update cdx-manager using the installer that matches how it was installed |
@@ -0,0 +1,28 @@
1
+ # CDX Manager 0.12.2
2
+
3
+ ## Highlights
4
+
5
+ - `cdx ready` now exits successfully when no future reset is known, clearly reporting that no notification was scheduled.
6
+ - `cdx doctor` can filter diagnostics by severity with `--severity OK|WARN|FAIL`.
7
+ - Unknown `cdx config <name>` sessions now explain how to inspect existing sessions or create the requested one.
8
+
9
+ ## Changes
10
+
11
+ ### Safer ready notifications
12
+
13
+ When `cdx ready` cannot find an upcoming session reset, it returns a successful unscheduled result instead of failing. Named reset notifications with no known reset remain explicit errors.
14
+
15
+ ### Filterable diagnostics
16
+
17
+ `cdx doctor --severity` accepts a comma-separated list of `OK`, `WARN`, and `FAIL` statuses. Text and JSON output retain accurate summaries for the filtered report.
18
+
19
+ ### Actionable configuration errors
20
+
21
+ Unknown-session errors from `cdx config` now direct users to `cdx configs` to inspect available sessions and `cdx add <name>` to create one.
22
+
23
+ ## Validation
24
+
25
+ - `npm run lint`
26
+ - `npm test`
27
+ - `logics-manager lint --require-status`
28
+ - `logics-manager audit --group-by-doc`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cdx-manager",
3
- "version": "0.12.1",
3
+ "version": "0.12.2",
4
4
  "description": "Terminal session manager for Codex and Claude accounts.",
5
5
  "license": "MIT",
6
6
  "author": "Alexandre Agostini",
package/pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "cdx-manager"
7
- version = "0.12.1"
7
+ version = "0.12.2"
8
8
  description = "Terminal session manager for Codex and Claude accounts."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
package/src/cli.py CHANGED
@@ -78,7 +78,7 @@ from .status_view import (
78
78
  )
79
79
  from .update_check import check_for_update, check_logics_manager_for_update
80
80
 
81
- VERSION = "0.12.1"
81
+ VERSION = "0.12.2"
82
82
 
83
83
  # Public surface: this module is a facade. Names below are imported above
84
84
  # purely to be re-exported (consumed by tests and external callers); listing
@@ -216,7 +216,7 @@ def _print_help(use_color=False):
216
216
  f" {_style('cdx clean profiles (--tmp|--old-logs DAYS) [--yes] [--json]', '36', use_color)}",
217
217
  f" {_style('cdx export <file> [--include-auth] [--sessions a,b] [--passphrase-env VAR|--passphrase-stdin] [--force] [--json]', '36', use_color)}",
218
218
  f" {_style('cdx import <file> [--sessions a,b] [--passphrase-env VAR|--passphrase-stdin] [--force|--merge] [--json]', '36', use_color)}",
219
- f" {_style('cdx doctor [--json]', '36', use_color)}",
219
+ f" {_style('cdx doctor [--severity OK|WARN|FAIL[,OK|WARN|FAIL...]] [--json]', '36', use_color)}",
220
220
  f" {_style('cdx repair [--dry-run] [--force] [--json]', '36', use_color)}",
221
221
  f" {_style('cdx view [--json]', '36', use_color)}",
222
222
  f" {_style('cdx update [all] [--check] [--yes] [--json] [--version TAG]', '36', use_color)}",
package/src/cli_args.py CHANGED
@@ -12,7 +12,7 @@ from .errors import CdxError
12
12
  from .provider_runtime import _normalize_reasoning_effort
13
13
 
14
14
  STATUS_USAGE = "Usage: cdx status [--json] [--refresh|--cached] [--timeout SECONDS] | cdx status --small|-s [--refresh|--cached] [--timeout SECONDS] | cdx status <name> [--json] [--refresh|--cached] [--timeout SECONDS]"
15
- DOCTOR_USAGE = "Usage: cdx doctor [--json]"
15
+ DOCTOR_USAGE = "Usage: cdx doctor [--severity OK|WARN|FAIL[,OK|WARN|FAIL...]] [--json]"
16
16
  DISK_USAGE = "Usage: cdx disk [profiles] [--candidates] [--json]"
17
17
  REPAIR_USAGE = "Usage: cdx repair [--dry-run] [--force] [--json]"
18
18
  UPDATE_USAGE = "Usage: cdx update [all] [--check] [--yes] [--json] [--version TAG]"
@@ -46,6 +46,35 @@ def _parse_json_flag(args):
46
46
  return json_flag, cleaned
47
47
 
48
48
 
49
+ def _parse_doctor_args(args):
50
+ parsed = {"json": False, "severity": None}
51
+ index = 0
52
+ while index < len(args):
53
+ arg = args[index]
54
+ if arg == "--json":
55
+ parsed["json"] = True
56
+ index += 1
57
+ continue
58
+ if arg == "--severity":
59
+ if parsed["severity"] is not None or index + 1 >= len(args):
60
+ raise CdxError(DOCTOR_USAGE)
61
+ value = args[index + 1]
62
+ index += 2
63
+ elif arg.startswith("--severity="):
64
+ if parsed["severity"] is not None:
65
+ raise CdxError(DOCTOR_USAGE)
66
+ value = arg.split("=", 1)[1]
67
+ index += 1
68
+ else:
69
+ raise CdxError(DOCTOR_USAGE)
70
+
71
+ values = [item.strip().upper() for item in value.split(",")]
72
+ if not values or any(item not in {"OK", "WARN", "FAIL"} for item in values):
73
+ raise CdxError(DOCTOR_USAGE)
74
+ parsed["severity"] = ",".join(dict.fromkeys(values))
75
+ return parsed
76
+
77
+
49
78
  def _parse_flag_args(args, schema, usage, positionals_key=None, max_positionals=0):
50
79
  parsed = {spec["key"]: spec.get("default") for spec in schema.values()}
51
80
  positionals = []
@@ -17,7 +17,6 @@ from .cli_args import (
17
17
  CAN_RESUME_USAGE,
18
18
  CONTEXT_USAGE,
19
19
  DISK_USAGE,
20
- DOCTOR_USAGE,
21
20
  HANDOFF_USAGE,
22
21
  LAST_USAGE,
23
22
  MEMORY_USAGE,
@@ -33,6 +32,7 @@ from .cli_args import (
33
32
  _parse_config_args,
34
33
  _parse_configs_args,
35
34
  _parse_copy_args,
35
+ _parse_doctor_args,
36
36
  _parse_export_args,
37
37
  _parse_flag_args,
38
38
  _parse_history_args,
@@ -98,7 +98,7 @@ from .context_store import (
98
98
  )
99
99
  from .errors import CdxError
100
100
  from .fs_utils import atomic_write
101
- from .health import collect_health_report, format_health_report
101
+ from .health import collect_health_report, filter_health_report, format_health_report
102
102
  from .notify import (
103
103
  format_notify_event,
104
104
  format_scheduled_notification,
@@ -1700,7 +1700,10 @@ def handle_config(rest, ctx):
1700
1700
  parsed = _parse_config_args(rest)
1701
1701
  session = ctx["service"]["get_session"](parsed["name"])
1702
1702
  if not session:
1703
- raise CdxError(f"Unknown session: {parsed['name']}")
1703
+ raise CdxError(
1704
+ f"Unknown session: {parsed['name']}. Run cdx configs to inspect existing sessions or "
1705
+ f"cdx add {parsed['name']} to create it."
1706
+ )
1704
1707
  message = f"Launch settings for {parsed['name']}"
1705
1708
  if parsed["json"]:
1706
1709
  _write_json(ctx, _json_success("config", message, session=session, launch=session.get("launch") or {}))
@@ -1893,17 +1896,15 @@ def handle_clean(rest, ctx):
1893
1896
 
1894
1897
 
1895
1898
  def handle_doctor(rest, ctx):
1896
- json_flag = "--json" in rest
1897
- unknown = [arg for arg in rest if arg != "--json"]
1898
- if unknown:
1899
- raise CdxError(DOCTOR_USAGE)
1899
+ parsed = _parse_doctor_args(rest)
1900
1900
  report = collect_health_report(
1901
1901
  ctx["service"],
1902
1902
  ctx["service"]["base_dir"],
1903
1903
  env=ctx.get("env"),
1904
1904
  spawn_sync=ctx.get("spawn_sync"),
1905
1905
  )
1906
- if json_flag:
1906
+ report = filter_health_report(report, parsed["severity"])
1907
+ if parsed["json"]:
1907
1908
  _write_json(ctx, _json_success("doctor", "Collected health report", report=report))
1908
1909
  else:
1909
1910
  ctx["out"](f"{format_health_report(report, use_color=ctx['use_color'])}\n")
package/src/health.py CHANGED
@@ -363,6 +363,19 @@ def summarize_health(issues):
363
363
  }
364
364
 
365
365
 
366
+ def filter_health_report(report, severity):
367
+ if severity is None:
368
+ return report
369
+ severities = set(severity.split(","))
370
+ issues = [issue for issue in report["issues"] if issue["status"] in severities]
371
+ return {
372
+ **report,
373
+ "issues": issues,
374
+ "summary": summarize_health(issues),
375
+ "severity": severity,
376
+ }
377
+
378
+
366
379
  def format_health_report(report, use_color=False):
367
380
  rows = [["STATUS", "CHECK", "MESSAGE"]]
368
381
  for issue in report["issues"]:
package/src/notify.py CHANGED
@@ -221,6 +221,13 @@ def schedule_notification_event(base_dir, parsed, event, spawn_sync=None, env=No
221
221
  now_fn = now_fn or time.time
222
222
  target_timestamp = event.get("target_timestamp")
223
223
  if target_timestamp is None:
224
+ if parsed["mode"] == "next-ready":
225
+ return {
226
+ "scheduled": False,
227
+ "backend": "none",
228
+ "message": event["message"],
229
+ "target_timestamp": None,
230
+ }
224
231
  raise CdxError(f"Cannot schedule notification: {event['message']}")
225
232
  if target_timestamp <= now_fn():
226
233
  return {