thwip-cli 1.0.2__tar.gz → 1.0.3__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.
- {thwip_cli-1.0.2 → thwip_cli-1.0.3}/PKG-INFO +1 -1
- {thwip_cli-1.0.2 → thwip_cli-1.0.3}/pyproject.toml +1 -1
- {thwip_cli-1.0.2 → thwip_cli-1.0.3}/thwip/__init__.py +1 -1
- {thwip_cli-1.0.2 → thwip_cli-1.0.3}/thwip/agents/base.py +2 -0
- {thwip_cli-1.0.2 → thwip_cli-1.0.3}/thwip/agents/claude_agent.py +6 -0
- {thwip_cli-1.0.2 → thwip_cli-1.0.3}/thwip/agents/google_agent.py +30 -1
- {thwip_cli-1.0.2 → thwip_cli-1.0.3}/thwip/agents/openai_agent.py +29 -6
- {thwip_cli-1.0.2 → thwip_cli-1.0.3}/thwip/cli.py +137 -24
- {thwip_cli-1.0.2 → thwip_cli-1.0.3}/.github/workflows/publish.yml +0 -0
- {thwip_cli-1.0.2 → thwip_cli-1.0.3}/.gitignore +0 -0
- {thwip_cli-1.0.2 → thwip_cli-1.0.3}/README.md +0 -0
- {thwip_cli-1.0.2 → thwip_cli-1.0.3}/install.sh +0 -0
- {thwip_cli-1.0.2 → thwip_cli-1.0.3}/tests/test_agents.py +0 -0
- {thwip_cli-1.0.2 → thwip_cli-1.0.3}/tests/test_detector.py +0 -0
- {thwip_cli-1.0.2 → thwip_cli-1.0.3}/tests/test_session.py +0 -0
- {thwip_cli-1.0.2 → thwip_cli-1.0.3}/tests/test_tools.py +0 -0
- {thwip_cli-1.0.2 → thwip_cli-1.0.3}/thwip/__main__.py +0 -0
- {thwip_cli-1.0.2 → thwip_cli-1.0.3}/thwip/agents/__init__.py +0 -0
- {thwip_cli-1.0.2 → thwip_cli-1.0.3}/thwip/agents/deepseek_agent.py +0 -0
- {thwip_cli-1.0.2 → thwip_cli-1.0.3}/thwip/agents/groq_agent.py +0 -0
- {thwip_cli-1.0.2 → thwip_cli-1.0.3}/thwip/agents/ollama_agent.py +0 -0
- {thwip_cli-1.0.2 → thwip_cli-1.0.3}/thwip/agents/openrouter_agent.py +0 -0
- {thwip_cli-1.0.2 → thwip_cli-1.0.3}/thwip/config.py +0 -0
- {thwip_cli-1.0.2 → thwip_cli-1.0.3}/thwip/detector.py +0 -0
- {thwip_cli-1.0.2 → thwip_cli-1.0.3}/thwip/limits.py +0 -0
- {thwip_cli-1.0.2 → thwip_cli-1.0.3}/thwip/session.py +0 -0
- {thwip_cli-1.0.2 → thwip_cli-1.0.3}/thwip/shortcuts.py +0 -0
- {thwip_cli-1.0.2 → thwip_cli-1.0.3}/thwip/theme.py +0 -0
- {thwip_cli-1.0.2 → thwip_cli-1.0.3}/thwip/tools/__init__.py +0 -0
- {thwip_cli-1.0.2 → thwip_cli-1.0.3}/thwip/tools/code_runner.py +0 -0
- {thwip_cli-1.0.2 → thwip_cli-1.0.3}/thwip/tools/file_editor.py +0 -0
- {thwip_cli-1.0.2 → thwip_cli-1.0.3}/thwip/tools/git_ops.py +0 -0
- {thwip_cli-1.0.2 → thwip_cli-1.0.3}/thwip/tools/terminal.py +0 -0
- {thwip_cli-1.0.2 → thwip_cli-1.0.3}/thwip/utils.py +0 -0
- {thwip_cli-1.0.2 → thwip_cli-1.0.3}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: thwip-cli
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.3
|
|
4
4
|
Summary: Universal coding agent multiplexer: detect, switch, and route between AI coding agents seamlessly
|
|
5
5
|
Project-URL: Homepage, https://github.com/tanmayhutt/thwip-cli
|
|
6
6
|
Project-URL: Repository, https://github.com/tanmayhutt/thwip-cli
|
|
@@ -158,6 +158,8 @@ class ModelInfo:
|
|
|
158
158
|
"""Information about a specific model."""
|
|
159
159
|
id: str # "claude-sonnet-4"
|
|
160
160
|
name: str # "Claude Sonnet 4"
|
|
161
|
+
tier: str = "balanced" # "flagship" (high), "balanced" (mid), "fast" (low-cost)
|
|
162
|
+
description: str = "" # e.g. "Flagship reasoning with 1M context"
|
|
161
163
|
context_window: int = 0 # Max tokens
|
|
162
164
|
max_output: int = 0 # Max output tokens
|
|
163
165
|
supports_tools: bool = True
|
|
@@ -59,6 +59,8 @@ class ClaudeAgent(BaseAgent):
|
|
|
59
59
|
ModelInfo(
|
|
60
60
|
id="claude-sonnet-4",
|
|
61
61
|
name="Claude Sonnet 4",
|
|
62
|
+
tier="flagship",
|
|
63
|
+
description="Flagship coding intelligence with hybrid thinking and tool precision",
|
|
62
64
|
context_window=200_000,
|
|
63
65
|
max_output=16_384,
|
|
64
66
|
supports_tools=True,
|
|
@@ -72,6 +74,8 @@ class ClaudeAgent(BaseAgent):
|
|
|
72
74
|
ModelInfo(
|
|
73
75
|
id="claude-opus-4",
|
|
74
76
|
name="Claude Opus 4",
|
|
77
|
+
tier="flagship",
|
|
78
|
+
description="Ultra high capability reasoning for complex systems engineering",
|
|
75
79
|
context_window=200_000,
|
|
76
80
|
max_output=32_000,
|
|
77
81
|
supports_tools=True,
|
|
@@ -84,6 +88,8 @@ class ClaudeAgent(BaseAgent):
|
|
|
84
88
|
ModelInfo(
|
|
85
89
|
id="claude-haiku-3.5",
|
|
86
90
|
name="Claude Haiku 3.5",
|
|
91
|
+
tier="fast",
|
|
92
|
+
description="Fastest lightweight Claude model for rapid iteration",
|
|
87
93
|
context_window=200_000,
|
|
88
94
|
max_output=8_192,
|
|
89
95
|
supports_tools=True,
|
|
@@ -59,6 +59,8 @@ class GoogleAgent(BaseAgent):
|
|
|
59
59
|
ModelInfo(
|
|
60
60
|
id="gemini-2.5-pro",
|
|
61
61
|
name="Gemini 2.5 Pro",
|
|
62
|
+
tier="flagship",
|
|
63
|
+
description="Flagship multimodal model with 1M context and advanced reasoning",
|
|
62
64
|
context_window=1_048_576,
|
|
63
65
|
max_output=65_536,
|
|
64
66
|
supports_tools=True,
|
|
@@ -72,6 +74,8 @@ class GoogleAgent(BaseAgent):
|
|
|
72
74
|
ModelInfo(
|
|
73
75
|
id="gemini-2.5-flash",
|
|
74
76
|
name="Gemini 2.5 Flash",
|
|
77
|
+
tier="balanced",
|
|
78
|
+
description="High-speed balanced model optimized for daily coding workflows",
|
|
75
79
|
context_window=1_048_576,
|
|
76
80
|
max_output=65_536,
|
|
77
81
|
supports_tools=True,
|
|
@@ -81,6 +85,20 @@ class GoogleAgent(BaseAgent):
|
|
|
81
85
|
pricing_input=0.15,
|
|
82
86
|
pricing_output=0.60,
|
|
83
87
|
),
|
|
88
|
+
ModelInfo(
|
|
89
|
+
id="gemini-2.5-flash-lite",
|
|
90
|
+
name="Gemini 2.5 Flash Lite",
|
|
91
|
+
tier="fast",
|
|
92
|
+
description="Ultra lightweight and cost-effective model for rapid tasks",
|
|
93
|
+
context_window=1_048_576,
|
|
94
|
+
max_output=65_536,
|
|
95
|
+
supports_tools=True,
|
|
96
|
+
supports_streaming=True,
|
|
97
|
+
supports_vision=False,
|
|
98
|
+
supports_thinking=False,
|
|
99
|
+
pricing_input=0.075,
|
|
100
|
+
pricing_output=0.30,
|
|
101
|
+
),
|
|
84
102
|
]
|
|
85
103
|
|
|
86
104
|
def __init__(self, api_key: str | None = None) -> None:
|
|
@@ -171,7 +189,7 @@ class GoogleAgent(BaseAgent):
|
|
|
171
189
|
pass
|
|
172
190
|
break
|
|
173
191
|
|
|
174
|
-
#
|
|
192
|
+
# Check for Antigravity IDE or Gemini app (macOS app)
|
|
175
193
|
if not info["path"]:
|
|
176
194
|
app_paths = [
|
|
177
195
|
Path("/Applications/Antigravity IDE.app"),
|
|
@@ -187,6 +205,17 @@ class GoogleAgent(BaseAgent):
|
|
|
187
205
|
info["version"] = "Installed"
|
|
188
206
|
break
|
|
189
207
|
|
|
208
|
+
# Check for Google Account in ~/.gemini/google_accounts.json
|
|
209
|
+
acc_file = Path.home() / ".gemini" / "google_accounts.json"
|
|
210
|
+
if acc_file.is_file():
|
|
211
|
+
try:
|
|
212
|
+
acc_data = json.loads(acc_file.read_text())
|
|
213
|
+
account = acc_data.get("active") or (acc_data.get("old") and acc_data["old"][0]) or ""
|
|
214
|
+
if account:
|
|
215
|
+
info["account"] = account
|
|
216
|
+
except Exception:
|
|
217
|
+
pass
|
|
218
|
+
|
|
190
219
|
return info
|
|
191
220
|
|
|
192
221
|
def get_subscription_info(self) -> SubscriptionInfo:
|
|
@@ -40,9 +40,9 @@ class OpenAIAgent(BaseAgent):
|
|
|
40
40
|
"""
|
|
41
41
|
|
|
42
42
|
name = "openai"
|
|
43
|
-
display_name = "
|
|
43
|
+
display_name = "ChatGPT / Codex"
|
|
44
44
|
company = "OpenAI"
|
|
45
|
-
description = "OpenAI's
|
|
45
|
+
description = "OpenAI's ChatGPT and Codex models with tool execution"
|
|
46
46
|
website = "https://platform.openai.com"
|
|
47
47
|
|
|
48
48
|
capabilities = {
|
|
@@ -59,6 +59,8 @@ class OpenAIAgent(BaseAgent):
|
|
|
59
59
|
ModelInfo(
|
|
60
60
|
id="gpt-4.1",
|
|
61
61
|
name="GPT-4.1",
|
|
62
|
+
tier="flagship",
|
|
63
|
+
description="Next-generation flagship with 1M context and advanced coding",
|
|
62
64
|
context_window=1_047_576,
|
|
63
65
|
max_output=32_768,
|
|
64
66
|
supports_tools=True,
|
|
@@ -71,6 +73,8 @@ class OpenAIAgent(BaseAgent):
|
|
|
71
73
|
ModelInfo(
|
|
72
74
|
id="o3",
|
|
73
75
|
name="O3",
|
|
76
|
+
tier="flagship",
|
|
77
|
+
description="Deep reasoning model with autonomous multi-step problem solving",
|
|
74
78
|
context_window=200_000,
|
|
75
79
|
max_output=100_000,
|
|
76
80
|
supports_tools=True,
|
|
@@ -79,9 +83,24 @@ class OpenAIAgent(BaseAgent):
|
|
|
79
83
|
pricing_input=2.0,
|
|
80
84
|
pricing_output=8.0,
|
|
81
85
|
),
|
|
86
|
+
ModelInfo(
|
|
87
|
+
id="gpt-4o",
|
|
88
|
+
name="GPT-4o",
|
|
89
|
+
tier="balanced",
|
|
90
|
+
description="High-speed multimodal flagship for omni-modal coding and vision",
|
|
91
|
+
context_window=128_000,
|
|
92
|
+
max_output=16_384,
|
|
93
|
+
supports_tools=True,
|
|
94
|
+
supports_streaming=True,
|
|
95
|
+
supports_vision=True,
|
|
96
|
+
pricing_input=2.50,
|
|
97
|
+
pricing_output=10.0,
|
|
98
|
+
),
|
|
82
99
|
ModelInfo(
|
|
83
100
|
id="o4-mini",
|
|
84
101
|
name="O4 Mini",
|
|
102
|
+
tier="balanced",
|
|
103
|
+
description="Fast and affordable reasoning model for everyday programming",
|
|
85
104
|
context_window=200_000,
|
|
86
105
|
max_output=100_000,
|
|
87
106
|
supports_tools=True,
|
|
@@ -91,19 +110,23 @@ class OpenAIAgent(BaseAgent):
|
|
|
91
110
|
pricing_output=4.40,
|
|
92
111
|
),
|
|
93
112
|
ModelInfo(
|
|
94
|
-
id="gpt-4o",
|
|
95
|
-
name="GPT-4o",
|
|
113
|
+
id="gpt-4o-mini",
|
|
114
|
+
name="GPT-4o Mini",
|
|
115
|
+
tier="fast",
|
|
116
|
+
description="Lightweight and low-latency model for fast script tasks",
|
|
96
117
|
context_window=128_000,
|
|
97
118
|
max_output=16_384,
|
|
98
119
|
supports_tools=True,
|
|
99
120
|
supports_streaming=True,
|
|
100
121
|
supports_vision=True,
|
|
101
|
-
pricing_input=
|
|
102
|
-
pricing_output=
|
|
122
|
+
pricing_input=0.15,
|
|
123
|
+
pricing_output=0.60,
|
|
103
124
|
),
|
|
104
125
|
ModelInfo(
|
|
105
126
|
id="codex-mini",
|
|
106
127
|
name="Codex Mini",
|
|
128
|
+
tier="fast",
|
|
129
|
+
description="Specialized fast code generation model",
|
|
107
130
|
context_window=200_000,
|
|
108
131
|
max_output=16_384,
|
|
109
132
|
supports_tools=True,
|
|
@@ -195,8 +195,11 @@ class ThwipCLI:
|
|
|
195
195
|
elif cmd in ("/agents", "/list"):
|
|
196
196
|
self.cmd_show_agents()
|
|
197
197
|
|
|
198
|
+
elif cmd in ("/key", "/auth", "/config"):
|
|
199
|
+
self.cmd_auth_config(arg1, arg2)
|
|
200
|
+
|
|
198
201
|
elif cmd in ("/models", "/m"):
|
|
199
|
-
self.cmd_show_models(arg1)
|
|
202
|
+
self.cmd_show_models(arg1, arg2)
|
|
200
203
|
|
|
201
204
|
elif cmd == "/tools":
|
|
202
205
|
self.cmd_show_tools()
|
|
@@ -298,7 +301,8 @@ class ThwipCLI:
|
|
|
298
301
|
("/about", "Display full About section, architecture, and navigation guide"),
|
|
299
302
|
("/switch [agent] [model]", "Switch active agent/model mid-conversation without losing context"),
|
|
300
303
|
("/agents", "Show all detected coding agents, company status & capabilities"),
|
|
301
|
-
("/models [agent]", "List
|
|
304
|
+
("/models [agent|tier]", "List models filtered by provider or tier (flagship, balanced, fast)"),
|
|
305
|
+
("/key [provider] [key]", "View or configure API keys saved in ~/.thwip/config.toml"),
|
|
302
306
|
("/tools", "List all universal file, terminal, and git tools"),
|
|
303
307
|
("/status", "Display current session, project, and token stats"),
|
|
304
308
|
("/limits", "View token usage, quota, and spend metrics"),
|
|
@@ -397,33 +401,142 @@ class ThwipCLI:
|
|
|
397
401
|
rows = [a.to_table_row() for a in self.registry.list_agents()]
|
|
398
402
|
console.print(render_agents_table(rows))
|
|
399
403
|
|
|
400
|
-
def cmd_show_models(self,
|
|
401
|
-
"""List models
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
404
|
+
def cmd_show_models(self, arg1: str = "", arg2: str = "") -> None:
|
|
405
|
+
"""List models by provider or tier (flagship, balanced, fast)."""
|
|
406
|
+
tier_filter = ""
|
|
407
|
+
agent_target = None
|
|
408
|
+
|
|
409
|
+
tier_aliases = {
|
|
410
|
+
"flagship": "flagship",
|
|
411
|
+
"high": "flagship",
|
|
412
|
+
"pro": "flagship",
|
|
413
|
+
"balanced": "balanced",
|
|
414
|
+
"mid": "balanced",
|
|
415
|
+
"flash": "balanced",
|
|
416
|
+
"fast": "fast",
|
|
417
|
+
"low": "fast",
|
|
418
|
+
"lite": "fast",
|
|
419
|
+
"mini": "fast",
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
if arg1.lower() in tier_aliases:
|
|
423
|
+
tier_filter = tier_aliases[arg1.lower()]
|
|
424
|
+
elif arg1:
|
|
425
|
+
agent_target = self.registry.get_agent(arg1)
|
|
426
|
+
if not agent_target:
|
|
427
|
+
print_error(f"Agent '{arg1}' not found.")
|
|
428
|
+
return
|
|
429
|
+
if arg2.lower() in tier_aliases:
|
|
430
|
+
tier_filter = tier_aliases[arg2.lower()]
|
|
431
|
+
|
|
432
|
+
if not agent_target and not tier_filter:
|
|
433
|
+
agent_target = self.current_agent
|
|
406
434
|
|
|
407
|
-
|
|
435
|
+
if agent_target:
|
|
436
|
+
agents_to_show = [agent_target]
|
|
437
|
+
title = f"Available Models for {agent_target.display_name}"
|
|
438
|
+
if tier_filter:
|
|
439
|
+
title += f" ({tier_filter.title()} Tier)"
|
|
440
|
+
else:
|
|
441
|
+
agents_to_show = self.registry.list_agents()
|
|
442
|
+
title = f"All {tier_filter.title()} Tier Models Across Providers"
|
|
443
|
+
|
|
444
|
+
table = Table(title=title, box=box.ROUNDED)
|
|
445
|
+
if not agent_target:
|
|
446
|
+
table.add_column("Provider", style="cyan")
|
|
408
447
|
table.add_column("Model ID", style="bold white")
|
|
409
|
-
table.add_column("Name", style="
|
|
410
|
-
table.add_column("
|
|
411
|
-
table.add_column("
|
|
448
|
+
table.add_column("Name", style="white")
|
|
449
|
+
table.add_column("Tier", style="bold")
|
|
450
|
+
table.add_column("Context", style="dim")
|
|
412
451
|
table.add_column("Thinking", style="magenta")
|
|
413
|
-
table.add_column("Price (In/Out
|
|
452
|
+
table.add_column("Price (In/Out 1M)", style="green")
|
|
453
|
+
|
|
454
|
+
tier_styles = {
|
|
455
|
+
"flagship": "[bold magenta]Flagship / High[/bold magenta]",
|
|
456
|
+
"balanced": "[bold cyan]Balanced / Mid[/bold cyan]",
|
|
457
|
+
"fast": "[bold green]Fast / Low[/bold green]",
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
for ag in agents_to_show:
|
|
461
|
+
for m in ag.available_models:
|
|
462
|
+
m_tier = getattr(m, "tier", "balanced")
|
|
463
|
+
if tier_filter and m_tier != tier_filter:
|
|
464
|
+
continue
|
|
465
|
+
|
|
466
|
+
ctx = f"{m.context_window:,}" if m.context_window else "-"
|
|
467
|
+
price = f"${m.pricing_input} / ${m.pricing_output}" if m.pricing_input else "Free"
|
|
468
|
+
tier_badge = tier_styles.get(m_tier, m_tier.title())
|
|
469
|
+
def_mark = " [dim](default)[/dim]" if m.is_default else ""
|
|
470
|
+
|
|
471
|
+
row = []
|
|
472
|
+
if not agent_target:
|
|
473
|
+
row.append(ag.company)
|
|
474
|
+
row.extend([
|
|
475
|
+
m.id + def_mark,
|
|
476
|
+
m.name,
|
|
477
|
+
tier_badge,
|
|
478
|
+
ctx,
|
|
479
|
+
"yes" if m.supports_thinking else "-",
|
|
480
|
+
price,
|
|
481
|
+
])
|
|
482
|
+
table.add_row(*row)
|
|
483
|
+
|
|
484
|
+
console.print(table)
|
|
485
|
+
console.print("[dim]Filter by tier: [bold]/models flagship[/bold], [bold]/models balanced[/bold], [bold]/models fast[/bold][/dim]\n")
|
|
486
|
+
|
|
487
|
+
def cmd_auth_config(self, provider: str = "", key: str = "") -> None:
|
|
488
|
+
"""View or set API credentials stored in ~/.thwip/config.toml."""
|
|
489
|
+
provider_map = {
|
|
490
|
+
"google": "google",
|
|
491
|
+
"gemini": "google",
|
|
492
|
+
"antigravity": "google",
|
|
493
|
+
"openai": "openai",
|
|
494
|
+
"chatgpt": "openai",
|
|
495
|
+
"codex": "openai",
|
|
496
|
+
"claude": "anthropic",
|
|
497
|
+
"anthropic": "anthropic",
|
|
498
|
+
"deepseek": "deepseek",
|
|
499
|
+
"groq": "groq",
|
|
500
|
+
"openrouter": "openrouter",
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
if provider and key:
|
|
504
|
+
target_prov = provider_map.get(provider.lower(), provider.lower())
|
|
505
|
+
self.config.keys[target_prov] = key
|
|
506
|
+
self.config.key_sources[target_prov] = "config.toml"
|
|
507
|
+
self.config.save()
|
|
508
|
+
|
|
509
|
+
# Refresh agent registry
|
|
510
|
+
self.registry = AgentRegistry(self.config)
|
|
511
|
+
agent = self.registry.get_agent(self.session.current_agent)
|
|
512
|
+
if agent:
|
|
513
|
+
self.current_agent = agent
|
|
514
|
+
print_success(f"API key for '{target_prov}' saved to ~/.thwip/config.toml")
|
|
515
|
+
return
|
|
516
|
+
|
|
517
|
+
# Show interactive auth overview
|
|
518
|
+
table = Table(title="thwip API Credentials & Authentication", box=box.ROUNDED)
|
|
519
|
+
table.add_column("Provider", style="bold white")
|
|
520
|
+
table.add_column("Auth Status", style="white")
|
|
521
|
+
table.add_column("Key Source", style="dim")
|
|
522
|
+
table.add_column("Config Command", style="cyan")
|
|
523
|
+
|
|
524
|
+
for prov, label in [
|
|
525
|
+
("google", "Google Gemini / Antigravity"),
|
|
526
|
+
("openai", "OpenAI / ChatGPT / Codex"),
|
|
527
|
+
("anthropic", "Anthropic Claude"),
|
|
528
|
+
("deepseek", "DeepSeek"),
|
|
529
|
+
("groq", "Groq"),
|
|
530
|
+
("openrouter", "OpenRouter"),
|
|
531
|
+
]:
|
|
532
|
+
has_key = bool(self.config.keys.get(prov))
|
|
533
|
+
source = self.config.key_sources.get(prov, "none")
|
|
534
|
+
status = "[bold green]Configured[/bold green]" if has_key else "[bold yellow]Missing[/bold yellow]"
|
|
535
|
+
cmd_hint = f"/key {prov} <your_api_key>"
|
|
536
|
+
table.add_row(label, status, source, cmd_hint)
|
|
414
537
|
|
|
415
|
-
for m in target.available_models:
|
|
416
|
-
ctx = f"{m.context_window:,}" if m.context_window else "-"
|
|
417
|
-
price = f"${m.pricing_input} / ${m.pricing_output}" if m.pricing_input else "Free / Local"
|
|
418
|
-
table.add_row(
|
|
419
|
-
m.id + (" (default)" if m.is_default else ""),
|
|
420
|
-
m.name,
|
|
421
|
-
ctx,
|
|
422
|
-
"yes" if m.supports_tools else "-",
|
|
423
|
-
"yes" if m.supports_thinking else "-",
|
|
424
|
-
price,
|
|
425
|
-
)
|
|
426
538
|
console.print(table)
|
|
539
|
+
console.print("[dim]To set a key: [bold white]/key <provider> <key>[/bold white] (e.g. /key google AIzaSy...)[/dim]\n")
|
|
427
540
|
|
|
428
541
|
def cmd_show_status(self) -> None:
|
|
429
542
|
"""Display status."""
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|