Skip to content

Commit 11a7c77

Browse files
Enhance CLI commands and improve user feedback
- Updated CLI commands (`init`, `client-check`, `server-check`, `get-logs`, `test-clientlog`, `test-serverlog`) to provide clearer prompts and error messages, improving user experience. - Introduced new functions for better handling of missing credentials and session filters. - Enhanced logging and feedback mechanisms with additional context and recovery hints. - Refactored command outputs to include more informative messages and structured logging. - Improved the overall modularity and clarity of the CLI command structure.
1 parent d5f6434 commit 11a7c77

19 files changed

Lines changed: 1600 additions & 142 deletions

auralogger/cli/aside_pools.py

Lines changed: 689 additions & 0 deletions
Large diffs are not rendered by default.

auralogger/cli/cli.py

Lines changed: 126 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,183 @@
1-
"""CLI entrypoint for `auralogger` (mirrors node/src/bin/auralogger.ts)."""
1+
"""CLI entrypoint for `auralogger` — parity with node/src/cli/bin/auralogger.ts."""
22

3+
import random
34
import sys
4-
from typing import TextIO
5-
6-
from auralogger.cli.cli_load_env import load_cli_env_files
5+
from typing import List, TextIO
6+
7+
from auralogger.cli.aside_pools import (
8+
BIN_UNKNOWN_COMMAND_TEMPLATES,
9+
BIN_USAGE_ASIDES,
10+
BIN_USAGE_LEGENDARY_ASIDES,
11+
BIN_USAGE_RARE_MULTI_ASIDES,
12+
CLI_VETERAN_USAGE_ASIDES,
13+
DEFAULT_SILENCE_ASIDE_CHANCE,
14+
ENV_SETUP_RECOVERY_ASIDES,
15+
WOLVERINE_NUDGE_ASIDES,
16+
classify_error_for_aside,
17+
format_aside_template,
18+
pick_adaptive_fatal_aside,
19+
pick_aside,
20+
pick_tiered_aside,
21+
)
22+
from auralogger.cli.cli_load_env import ensure_utf8_stdio, load_cli_env_files
23+
from auralogger.cli.cli_personality_state import (
24+
get_consecutive_failures,
25+
get_total_successful_commands,
26+
note_command_dispatch,
27+
record_cli_failure,
28+
record_cli_success,
29+
)
30+
from auralogger.cli.cli_style import bold, bold_hex, dim, hex_color, red, red_bold, white
31+
from auralogger.cli.cli_tone import maybe_print_generic_spice, print_aside, print_aside_maybe
732
from auralogger.cli.commands.client_check import run_client_check
833
from auralogger.cli.commands.get_logs_cmd import run_get_logs_command
934
from auralogger.cli.commands.init import run_init
1035
from auralogger.cli.commands.server_check import run_server_check
1136
from auralogger.cli.commands.test_clientlog import run_test_clientlog
1237
from auralogger.cli.commands.test_serverlog import run_test_serverlog
1338

39+
KNOWN_COMMANDS = {
40+
"init",
41+
"get-logs",
42+
"server-check",
43+
"client-check",
44+
"test-serverlog",
45+
"test-clientlog",
46+
}
47+
1448

1549
def print_usage(stream: TextIO = sys.stdout) -> None:
16-
print("Usage:", file=stream)
17-
print(" auralogger init", file=stream)
18-
print(" auralogger server-check", file=stream)
19-
print(" auralogger client-check", file=stream)
20-
print(" auralogger test-serverlog", file=stream)
21-
print(" auralogger test-clientlog", file=stream)
22-
print(" auralogger get-logs [filters...]", file=stream)
2350
print("", file=stream)
24-
print("See user-docs/commands.md (in the python package source tree) for filter syntax.", file=stream)
51+
print(
52+
bold_hex("#ffa657", "✨ Auralogger CLI") + dim(" — pick a command:"),
53+
file=stream,
54+
)
55+
print(
56+
hex_color("#7ee787", " init") + dim(" wire up secrets + copy-paste client config"),
57+
file=stream,
58+
)
59+
print(
60+
hex_color("#7ee787", " server-check") + dim(" make sure the server logger can talk"),
61+
file=stream,
62+
)
63+
print(
64+
hex_color("#7ee787", " client-check") + dim(" same vibes, browser-style pipe"),
65+
file=stream,
66+
)
67+
print(
68+
hex_color("#7ee787", " test-serverlog") + dim(" five fake server logs, just for kicks"),
69+
file=stream,
70+
)
71+
print(
72+
hex_color("#7ee787", " test-clientlog") + dim(" five fake client logs, same deal"),
73+
file=stream,
74+
)
75+
print(
76+
hex_color("#7ee787", " get-logs") + dim(" hunt past logs (filters optional)"),
77+
file=stream,
78+
)
79+
print("", file=stream)
80+
print(
81+
dim("Docs live on npm: auralogger-cli — filter cheat sheet is there."),
82+
file=stream,
83+
)
84+
veteran = get_total_successful_commands() >= 4 and random.random() < 0.28
85+
if veteran:
86+
a = pick_aside(CLI_VETERAN_USAGE_ASIDES)
87+
else:
88+
a = pick_tiered_aside(
89+
{
90+
"common": BIN_USAGE_ASIDES,
91+
"rare": BIN_USAGE_RARE_MULTI_ASIDES,
92+
"legendary": BIN_USAGE_LEGENDARY_ASIDES,
93+
}
94+
)
95+
print_aside_maybe(a["emoji"], a["line"], DEFAULT_SILENCE_ASIDE_CHANCE)
96+
print("", file=stream)
2597

2698

2799
def main() -> None:
100+
ensure_utf8_stdio()
28101
load_cli_env_files()
29102

30-
args = sys.argv[1:]
103+
args: List[str] = sys.argv[1:]
31104
command = args[0] if args else None
32105

33106
if not command:
34107
print_usage()
35108
return
36109

110+
if command not in KNOWN_COMMANDS:
111+
record_cli_failure()
112+
print(
113+
red("🤔 Hmm, never heard of ") + bold(command) + red("."),
114+
file=sys.stderr,
115+
)
116+
t = pick_aside(BIN_UNKNOWN_COMMAND_TEMPLATES)
117+
print_aside_maybe(
118+
t["emoji"],
119+
format_aside_template(t["line"], {"cmd": command}),
120+
DEFAULT_SILENCE_ASIDE_CHANCE,
121+
)
122+
print_usage(sys.stderr)
123+
sys.exit(1)
124+
125+
note_command_dispatch(command)
126+
37127
if command == "init":
38128
run_init()
129+
record_cli_success(command)
39130
return
40131

41132
if command == "get-logs":
42133
run_get_logs_command(args)
134+
record_cli_success(command)
43135
return
44136

45137
if command == "server-check":
46138
run_server_check()
139+
record_cli_success(command)
47140
return
48141

49142
if command == "client-check":
50143
run_client_check()
144+
record_cli_success(command)
51145
return
52146

53147
if command == "test-serverlog":
54148
run_test_serverlog()
149+
record_cli_success(command)
55150
return
56151

57152
if command == "test-clientlog":
58153
run_test_clientlog()
154+
record_cli_success(command)
59155
return
60156

61-
print(f"Unknown command: {command}", file=sys.stderr)
62-
print(
63-
"Valid commands: init, server-check, client-check, test-serverlog, test-clientlog, get-logs",
64-
file=sys.stderr,
65-
)
66-
print_usage(sys.stderr)
67-
sys.exit(1)
68-
69157

70158
def _entrypoint() -> None:
159+
ensure_utf8_stdio()
71160
try:
72161
main()
73162
except SystemExit:
74163
raise
75164
except Exception as exc:
76-
print(
77-
f"auralogger: {exc}",
78-
file=sys.stderr,
79-
)
165+
record_cli_failure()
166+
message = str(exc) if isinstance(exc, Exception) else repr(exc)
167+
print("", file=sys.stderr)
168+
print(red_bold("💥 That didn't work."), file=sys.stderr)
169+
print(dim(" ") + white(message), file=sys.stderr)
170+
fails = get_consecutive_failures()
171+
if fails >= 2 and random.random() < 0.45:
172+
n = pick_aside(WOLVERINE_NUDGE_ASIDES)
173+
print_aside(n["emoji"], n["line"])
174+
aside = pick_adaptive_fatal_aside(fails, message)
175+
print_aside_maybe(aside["emoji"], aside["line"], 0.08)
176+
err_kind = classify_error_for_aside(message)
177+
if err_kind in ("network", "auth-env") and random.random() < 0.42:
178+
e = pick_aside(ENV_SETUP_RECOVERY_ASIDES)
179+
print_aside(e["emoji"], e["line"])
180+
maybe_print_generic_spice()
80181
sys.exit(1)
81182

82183

auralogger/cli/cli_auth.py

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@
55
from dataclasses import dataclass
66
from typing import Any, Dict, cast
77

8+
from auralogger.cli.aside_pools import (
9+
ENV_RECOVERY_HINT_PLAIN,
10+
PROMPT_MISSING_CREDENTIAL_TEMPLATES,
11+
format_aside_template,
12+
pick_aside,
13+
)
14+
from auralogger.cli.cli_style import cyan
15+
from auralogger.cli.cli_tone import print_aside
816
from auralogger.server.proj_auth import fetch_proj_auth_payload
917
from auralogger.utils.env_config import ENV_PROJECT_TOKEN
1018
from auralogger.utils.env_config import (
@@ -23,19 +31,27 @@ class CliProjectContext:
2331
session: str
2432

2533

34+
def _print_missing_credential_hint(env_key: str) -> None:
35+
print()
36+
t = pick_aside(PROMPT_MISSING_CREDENTIAL_TEMPLATES)
37+
print_aside(t["emoji"], format_aside_template(t["line"], {"envKey": env_key}))
38+
39+
2640
def prompt_for_project_token() -> str:
27-
entered = input(f"Paste {ENV_PROJECT_TOKEN} (your project token): ")
41+
_print_missing_credential_hint(ENV_PROJECT_TOKEN)
42+
entered = input(cyan("🔐 ") + f"Paste {ENV_PROJECT_TOKEN} (your project token): ")
2843
token = entered.strip()
2944
if not token:
30-
raise ValueError("Project token cannot be empty.")
45+
raise ValueError(f"Project token cannot be empty. {ENV_RECOVERY_HINT_PLAIN}")
3146
return token
3247

3348

3449
def prompt_for_user_secret() -> str:
35-
entered = input(f"Paste {ENV_USER_SECRET} (your user secret): ")
50+
_print_missing_credential_hint(ENV_USER_SECRET)
51+
entered = input(cyan("🙍 ") + f"Paste {ENV_USER_SECRET} (your user secret): ")
3652
secret = entered.strip()
3753
if not secret:
38-
raise ValueError("User secret cannot be empty.")
54+
raise ValueError(f"User secret cannot be empty. {ENV_RECOVERY_HINT_PLAIN}")
3955
return secret
4056

4157

@@ -67,7 +83,8 @@ def resolve_project_context_for_cli_checks() -> CliProjectContext:
6783
session = session_raw.strip() if isinstance(session_raw, str) else ""
6884
if not project_id or not session:
6985
raise ValueError(
70-
f"{ENV_PROJECT_TOKEN} looks invalid, or proj_auth did not return project_id/session."
86+
f"{ENV_PROJECT_TOKEN} looks invalid, or proj_auth did not return project_id/session. "
87+
f"{ENV_RECOVERY_HINT_PLAIN}"
7188
)
7289

7390
return CliProjectContext(

auralogger/cli/cli_load_env.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,28 @@
55
code stays free of filesystem env loading (same split as the Node package).
66
"""
77

8+
import io
89
import os
10+
import sys
911
from pathlib import Path
1012
from typing import Optional
1113

1214
from dotenv import load_dotenv
1315

1416

17+
def ensure_utf8_stdio() -> None:
18+
"""Match Node/chalk emoji output on Windows (cp1252 default breaks Unicode)."""
19+
for stream in (sys.stdout, sys.stderr):
20+
if isinstance(stream, io.StringIO):
21+
continue
22+
reconf = getattr(stream, "reconfigure", None)
23+
if callable(reconf):
24+
try:
25+
reconf(encoding="utf-8", errors="replace")
26+
except (OSError, ValueError, AttributeError, TypeError):
27+
pass
28+
29+
1530
def load_cli_env_files(cwd: Optional[str] = None) -> None:
1631
base = Path(cwd or os.getcwd())
1732
verbose = os.environ.get("DOTENV_CONFIG_QUIET") == "false"
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
"""In-process CLI session memory — parity with node/src/cli/utility/cli-personality-state.ts."""
2+
3+
from __future__ import annotations
4+
5+
from typing import Dict
6+
7+
_consecutive_failures = 0
8+
_attempt_count_by_command: Dict[str, int] = {}
9+
_success_count_by_command: Dict[str, int] = {}
10+
11+
12+
def note_command_dispatch(command: str) -> None:
13+
_attempt_count_by_command[command] = _attempt_count_by_command.get(command, 0) + 1
14+
15+
16+
def get_command_attempt_count(command: str) -> int:
17+
return _attempt_count_by_command.get(command, 0)
18+
19+
20+
def record_cli_success(command: str) -> None:
21+
global _consecutive_failures
22+
_consecutive_failures = 0
23+
_success_count_by_command[command] = _success_count_by_command.get(command, 0) + 1
24+
25+
26+
def record_cli_failure() -> None:
27+
global _consecutive_failures
28+
_consecutive_failures += 1
29+
30+
31+
def get_consecutive_failures() -> int:
32+
return _consecutive_failures
33+
34+
35+
def get_successful_run_count(command: str) -> int:
36+
return _success_count_by_command.get(command, 0)
37+
38+
39+
def get_total_successful_commands() -> int:
40+
return sum(_success_count_by_command.values())

0 commit comments

Comments
 (0)