File size: 4,431 Bytes
0cac9cf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
"""CLI argument parsing and interactive command normalization helpers."""

from rich.console import Console
from rich.prompt import Prompt
from rich.panel import Panel
from rich.table import Table
from rich import box

from .config import CliOptions


def print_help(console: Console) -> None:
	"""Render CLI usage and available command-line options."""
	help_table = Table(box=box.ROUNDED, show_header=False, show_edge=True, border_style="bright_magenta", pad_edge=False)
	help_table.add_column("Option", style="bold cyan", no_wrap=True)
	help_table.add_column("Description", style="white")
	help_table.add_row("-h, --help", "Show this help message and exit")
	help_table.add_row("-s, --setup", "Reconfigure provider/API key/model and save settings")
	help_table.add_row("-p, --prompt", "Prompt text to send to the configured LLM")
	help_table.add_row("-q, --quit", "Exit the session")

	console.print(
		Panel(
			help_table,
			title="[bold magenta]QueryQuest CLI[/bold magenta]",
			border_style="bright_blue",
			box=box.DOUBLE,
			padding=(1, 2),
		)
	)
	console.print(
		Panel(
			"[bold]Usage[/bold]\n[cyan]QQ[/cyan] [yellow]-s|--setup[/yellow] [yellow]-p|--prompt[/yellow] <text> [yellow]-q|--quit[/yellow]",
			border_style="bright_green",
			box=box.ROUNDED,
			padding=(0, 1),
		)
	)


def print_banner(console: Console) -> None:
	"""Render a short welcome banner for the interactive CLI."""
	console.print(
		Panel(
			"[bold bright_magenta]QueryQuest[/bold bright_magenta]\n"
			"[cyan]Your personal Excel agent[/cyan]",
			border_style="bright_magenta",
			box=box.ROUNDED,
			padding=(1, 2),
		)
	)


def is_quit_command(text: str) -> bool:
	"""Return True when text matches one of the accepted quit commands."""
	value = text.strip().lower()
	return value in {
		"-q",
		"--quit",
		"qq -q",
		"qq --quit",
		"queryquest -q",
		"queryquest --quit",
	}


def normalize_prompt_input(text: str) -> tuple[str, bool]:
	"""Normalize inline prompt syntax and detect empty `-p/--prompt` usage."""
	value = text.strip()
	if value in ("-p", "--prompt"):
		return "", True
	if value.startswith("-p ") or value.startswith("--prompt "):
		parts = value.split(maxsplit=1)
		if len(parts) == 2:
			return parts[1].strip(), False
	return value, False


def parse_args(argv: list[str], console: Console) -> CliOptions:
	"""Parse command-line args, with interactive fallback for unknown/missing input."""
	if not argv:
		user_input = Prompt.ask("How can I help you today? (Tip: run [cyan]QQ -h[/cyan] for help.)", console=console).strip()

		# Allow command-style entries in interactive mode, e.g. "qq -h" or "QQ -s".
		parts = user_input.split(maxsplit=1)
		has_prefix = bool(parts and parts[0].lower() in ("qq", "queryquest"))
		if has_prefix:
			user_input = parts[1].strip() if len(parts) == 2 else ""

		if not has_prefix and user_input.startswith("-"):
			console.print("Please prefix options with [cyan]qq[/cyan] or [cyan]QQ[/cyan] (example: [cyan]qq -h[/cyan]).")
			return parse_args([], console)

		if user_input in ("-h", "--help"):
			print_help(console)
			raise SystemExit(0)
		if is_quit_command(user_input):
			raise SystemExit(0)
		if user_input in ("-s", "--setup"):
			return CliOptions(setup=True, prompt=None)
		if user_input in ("-p", "--prompt"):
			return CliOptions(setup=False, prompt=Prompt.ask("Prompt", console=console).strip())
		if user_input.startswith("-p ") or user_input.startswith("--prompt "):
			parts = user_input.split(maxsplit=1)
			if len(parts) == 2:
				return CliOptions(setup=False, prompt=parts[1].strip())

		return CliOptions(setup=False, prompt=user_input)

	setup = False
	prompt: str | None = None

	i = 0
	while i < len(argv):
		arg = argv[i]
		if arg in ("-h", "--help"):
			print_help(console)
			raise SystemExit(0)
		if arg in ("-q", "--quit"):
			raise SystemExit(0)
		if arg in ("-s", "--setup"):
			setup = True
			i += 1
			continue
		if arg in ("-p", "--prompt"):
			i += 1
			if i >= len(argv):
				console.print("Prompt value was missing. Switching to interactive mode selection.")
				return parse_args([], console)
			prompt = argv[i]
			i += 1
			continue
		console.print(f"Unknown argument '{arg}'. Switching to interactive mode selection.")
		return parse_args([], console)

	return CliOptions(setup=setup, prompt=prompt)


def main() -> None:
	"""Console entrypoint that delegates to the application bootstrap."""
	from .app import main as app_main

	app_main()