code-puppy 0.0.333__py3-none-any.whl → 0.0.334__py3-none-any.whl

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.
code_puppy/cli_runner.py CHANGED
@@ -364,6 +364,7 @@ async def interactive_mode(message_renderer, initial_command: str = None) -> Non
364
364
  emit_system_message(
365
365
  "Use /diff to configure diff highlighting colors for file changes."
366
366
  )
367
+ emit_system_message("To re-run the tutorial, use /tutorial.")
367
368
  try:
368
369
  from code_puppy.command_line.motd import print_motd
369
370
 
@@ -46,7 +46,7 @@ def handle_show_command(command: str) -> bool:
46
46
  get_use_dbos,
47
47
  get_yolo_mode,
48
48
  )
49
- from code_puppy.keymap import get_cancel_agent_key, get_cancel_agent_display_name
49
+ from code_puppy.keymap import get_cancel_agent_display_name
50
50
  from code_puppy.messaging import emit_info
51
51
 
52
52
  puppy_name = get_puppy_name()
@@ -115,6 +115,57 @@ def handle_motd_command(command: str) -> bool:
115
115
  return True
116
116
 
117
117
 
118
+ @register_command(
119
+ name="tutorial",
120
+ description="Run the interactive tutorial wizard",
121
+ usage="/tutorial",
122
+ category="core",
123
+ )
124
+ def handle_tutorial_command(command: str) -> bool:
125
+ """Run the interactive tutorial wizard.
126
+
127
+ Usage:
128
+ /tutorial - Run the tutorial (can be run anytime)
129
+ """
130
+ import asyncio
131
+ import concurrent.futures
132
+
133
+ from code_puppy.command_line.onboarding_wizard import (
134
+ reset_onboarding,
135
+ run_onboarding_wizard,
136
+ )
137
+ from code_puppy.config import set_model_name
138
+
139
+ # Always reset so user can re-run the tutorial anytime
140
+ reset_onboarding()
141
+
142
+ # Run the async wizard in a thread pool (same pattern as agent picker)
143
+ with concurrent.futures.ThreadPoolExecutor() as executor:
144
+ future = executor.submit(lambda: asyncio.run(run_onboarding_wizard()))
145
+ result = future.result(timeout=300) # 5 min timeout
146
+
147
+ if result == "chatgpt":
148
+ emit_info("🔐 Starting ChatGPT OAuth flow...")
149
+ from code_puppy.plugins.chatgpt_oauth.oauth_flow import run_oauth_flow
150
+
151
+ run_oauth_flow()
152
+ set_model_name("chatgpt-gpt-5.2-codex")
153
+ elif result == "claude":
154
+ emit_info("🔐 Starting Claude Code OAuth flow...")
155
+ from code_puppy.plugins.claude_code_oauth.register_callbacks import (
156
+ _perform_authentication,
157
+ )
158
+
159
+ _perform_authentication()
160
+ set_model_name("claude-code-claude-opus-4-5-20251101")
161
+ elif result == "completed":
162
+ emit_info("🎉 Tutorial complete! Happy coding!")
163
+ elif result == "skipped":
164
+ emit_info("⏭️ Tutorial skipped. Run /tutorial anytime!")
165
+
166
+ return True
167
+
168
+
118
169
  @register_command(
119
170
  name="exit",
120
171
  description="Exit interactive mode",
@@ -0,0 +1,416 @@
1
+ """Slide content for the onboarding wizard.
2
+
3
+ 🐶 Contains all slide definitions and content generators for the onboarding
4
+ wizard. Separated from the main wizard logic for maintainability.
5
+ """
6
+
7
+ from typing import List, Tuple
8
+
9
+ # ============================================================================
10
+ # Slide Data Constants
11
+ # ============================================================================
12
+
13
+ # Model subscription options for slide 1
14
+ MODEL_OPTIONS: List[Tuple[str, str, str]] = [
15
+ ("chatgpt", "ChatGPT Plus (Pro/Max)", "OAuth login with your ChatGPT subscription"),
16
+ ("claude", "Claude Code Pro/Max", "OAuth login with your Claude subscription"),
17
+ ("api_keys", "API Keys (OpenAI, Anthropic, etc.)", "Use your own API keys"),
18
+ ("openrouter", "OpenRouter", "Single API key for multiple providers"),
19
+ ("free", "Free tiers / Other providers", "Explore free and community models"),
20
+ ("skip", "Skip this", "Configure later with /set or /add_model"),
21
+ ]
22
+
23
+ # Common API keys users might want to configure
24
+ API_KEYS_INFO: List[Tuple[str, str]] = [
25
+ ("OPENAI_API_KEY", "OpenAI (GPT-5.2, GPT-5.2-codex)"),
26
+ ("ANTHROPIC_API_KEY", "Anthropic (Opus/Sonnet/Haiku 4.5)"),
27
+ ("GOOGLE_API_KEY", "Google (Gemini 3 Pro)"),
28
+ ("XAI_API_KEY", "xAI (Grok 4)"),
29
+ ("MINIMAX_API_KEY", "MiniMax (MiniMax M2.1)"),
30
+ ("OPENROUTER_API_KEY", "OpenRouter (100+ models)"),
31
+ ]
32
+
33
+ # Key agents to highlight
34
+ KEY_AGENTS: List[Tuple[str, str, str]] = [
35
+ ("planning-agent", "📋 Planning Agent", "Breaks down complex tasks"),
36
+ ("qa-kitten", "🐱 QA Kitten", "Browser automation with Playwright"),
37
+ ("security-auditor", "🛡️ Security Auditor", "Risk-based security review"),
38
+ ("code-reviewer", "🔍 Code Reviewer", "Holistic code review"),
39
+ ("python-programmer", "🐍 Python Programmer", "Modern Python specialist"),
40
+ ]
41
+
42
+
43
+ # ============================================================================
44
+ # Gradient Banner Generator
45
+ # ============================================================================
46
+
47
+
48
+ def get_gradient_banner() -> str:
49
+ """Generate the gradient CODE PUPPY banner.
50
+
51
+ Returns:
52
+ Rich markup string with gradient-colored pyfiglet banner.
53
+ """
54
+ try:
55
+ import pyfiglet
56
+
57
+ intro_lines = pyfiglet.figlet_format("CODE PUPPY", font="ansi_shadow").split(
58
+ "\n"
59
+ )
60
+
61
+ # Blue to cyan to green gradient (top to bottom)
62
+ gradient_colors = ["bright_blue", "bright_cyan", "bright_green"]
63
+
64
+ result_lines = []
65
+ for line_num, line in enumerate(intro_lines):
66
+ if line.strip():
67
+ color_idx = min(line_num // 2, len(gradient_colors) - 1)
68
+ color = gradient_colors[color_idx]
69
+ result_lines.append(f"[{color}]{line}[/{color}]")
70
+ else:
71
+ result_lines.append("")
72
+
73
+ return "\n".join(result_lines)
74
+ except ImportError:
75
+ # Fallback if pyfiglet not available
76
+ return (
77
+ "[bold bright_cyan]╔═══════════════════════════════╗\n"
78
+ "║ CODE PUPPY 🐶 ║\n"
79
+ "╚═══════════════════════════════╝[/bold bright_cyan]"
80
+ )
81
+
82
+
83
+ # ============================================================================
84
+ # Slide Content Generators
85
+ # ============================================================================
86
+
87
+
88
+ def slide_welcome() -> str:
89
+ """Slide 0: Welcome with gradient banner.
90
+
91
+ Returns:
92
+ Rich markup string for the slide content.
93
+ """
94
+ content = get_gradient_banner()
95
+ content += "\n\n"
96
+ content += "[bold white]Welcome to Code Puppy! 🐶[/bold white]\n\n"
97
+ content += "[dim]Let's get you set up for coding success![/dim]\n\n"
98
+ content += "[cyan]This wizard will help you configure:[/cyan]\n"
99
+ content += " • Model subscriptions & API keys\n"
100
+ content += " • MCP server integrations\n"
101
+ content += " • Appearance customization\n"
102
+ content += " • Agent system overview\n\n"
103
+ content += "[bold yellow]🎯 Navigation:[/bold yellow]\n"
104
+ content += "[green]→[/green] or [green]l[/green] Next slide\n"
105
+ content += "[green]←[/green] or [green]h[/green] Previous slide\n"
106
+ content += "[green]↓[/green] or [green]j[/green] Next option\n"
107
+ content += "[green]↑[/green] or [green]k[/green] Previous option\n"
108
+ content += "[green]Enter[/green] Select/proceed\n"
109
+ content += "[green]ESC[/green] Skip wizard\n\n"
110
+ content += "[dim]You can always run[/dim]\n"
111
+ content += "[cyan]/onboarding[/cyan] [dim]later![/dim]"
112
+
113
+ return content
114
+
115
+
116
+ def slide_model_subscription(
117
+ selected_option: int, options: List[Tuple[str, str]]
118
+ ) -> str:
119
+ """Slide 1: Model subscription selection.
120
+
121
+ Args:
122
+ selected_option: Index of the currently selected option.
123
+ options: List of (id, label) tuples for display.
124
+
125
+ Returns:
126
+ Rich markup string for the slide content.
127
+ """
128
+ content = "[bold cyan]📦 Model Subscription[/bold cyan]\n\n"
129
+ content += "[white]Do you have a model subscription?[/white]\n\n"
130
+
131
+ for i, (_, label) in enumerate(options):
132
+ if i == selected_option:
133
+ content += f"[bold green]▶ {label}[/bold green]\n"
134
+ else:
135
+ content += f"[dim] {label}[/dim]\n"
136
+
137
+ content += "\n\n"
138
+
139
+ # Dynamic content based on selection
140
+ selected_opt = options[selected_option][0] if options else None
141
+ if selected_opt == "chatgpt":
142
+ content += "[bold yellow]💡 ChatGPT Plus/Pro/Max[/bold yellow]\n\n"
143
+ content += "[green]OAuth login gives you access to:[/green]\n"
144
+ content += " • GPT-5.2\n"
145
+ content += " • GPT-5.2-codex\n"
146
+ content += " • No API key needed!\n"
147
+ elif selected_opt == "claude":
148
+ content += "[bold yellow]💡 Claude Code Pro/Max[/bold yellow]\n\n"
149
+ content += "[green]OAuth login gives you access to:[/green]\n"
150
+ content += " • Claude Opus 4.5\n"
151
+ content += " • Claude Sonnet 4.5\n"
152
+ content += " • Claude Haiku 4.5\n"
153
+ content += " • No API key needed!\n"
154
+ elif selected_opt == "api_keys":
155
+ content += "[bold yellow]💡 API Keys[/bold yellow]\n\n"
156
+ content += "[green]Use your own API keys for:[/green]\n"
157
+ content += " • OpenAI (GPT-5.2, GPT-5.2-codex)\n"
158
+ content += " • Anthropic (Opus/Sonnet/Haiku 4.5)\n"
159
+ content += " • Google (Gemini)\n"
160
+ content += " • And many more!\n"
161
+ elif selected_opt == "openrouter":
162
+ content += "[bold yellow]💡 OpenRouter[/bold yellow]\n\n"
163
+ content += "[green]Single API key for 100+ models:[/green]\n"
164
+ content += " • All major providers\n"
165
+ content += " • Pay-per-use pricing\n"
166
+ content += " • Great for exploration!\n"
167
+ elif selected_opt == "free":
168
+ content += "[bold yellow]💡 Free Tiers[/bold yellow]\n\n"
169
+ content += "[green]Explore free options:[/green]\n"
170
+ content += " • Google Gemini (free tier)\n"
171
+ content += " • Groq (fast inference)\n"
172
+ content += " • Local with Ollama\n"
173
+ else:
174
+ content += "[dim][Configure later using /set or /add_model][/dim]\n"
175
+
176
+ content += "\n\n[dim]Press → to continue[/dim]"
177
+ return content
178
+
179
+
180
+ def slide_api_keys(model_choice: str | None) -> str:
181
+ """Slide 2: API key setup information.
182
+
183
+ Args:
184
+ model_choice: User's model subscription selection from slide 1.
185
+
186
+ Returns:
187
+ Rich markup string for the slide content.
188
+ """
189
+ content = "[bold cyan]🔑 API Key Configuration[/bold cyan]\n\n"
190
+
191
+ if model_choice in ("chatgpt", "claude"):
192
+ content += "[green]✓ You selected OAuth login![/green]\n\n"
193
+ content += "[dim]After this wizard, we'll guide you\n"
194
+ content += "through the OAuth authentication flow.[/dim]\n\n"
195
+ content += "You can also add API keys for\n"
196
+ content += "additional providers:\n\n"
197
+ else:
198
+ content += "[white]Common API keys to configure:[/white]\n\n"
199
+
200
+ for key_name, description in API_KEYS_INFO:
201
+ content += f"[cyan]{key_name}[/cyan]\n"
202
+ content += f" [dim]{description}[/dim]\n"
203
+
204
+ content += "\n\n"
205
+ content += "[bold yellow]⚙️ Configuration[/bold yellow]\n\n"
206
+ content += "[green]Set API keys with:[/green]\n\n"
207
+ content += "[cyan]/set OPENAI_API_KEY=sk-...[/cyan]\n\n"
208
+ content += "[dim]Keys are stored securely in[/dim]\n"
209
+ content += "[dim]~/.config/code_puppy/puppy.cfg[/dim]\n\n"
210
+ content += "[green]Browse 1500+ models:[/green]\n"
211
+ content += "[cyan]/add_model[/cyan]\n"
212
+ content += "[dim]65+ providers available![/dim]"
213
+
214
+ return content
215
+
216
+
217
+ def slide_mcp_servers() -> str:
218
+ """Slide 3: MCP server information.
219
+
220
+ Returns:
221
+ Rich markup string for the slide content.
222
+ """
223
+ content = "[bold cyan]🔌 MCP Servers[/bold cyan]\n\n"
224
+ content += "[white]Extend Code Puppy with MCP![/white]\n\n"
225
+ content += "Model Context Protocol (MCP) lets you\n"
226
+ content += "add external tools and integrations.\n\n"
227
+ content += "[green]Commands:[/green]\n"
228
+ content += " [cyan]/mcp install[/cyan] - Browse curated catalog\n"
229
+ content += " [cyan]/mcp add[/cyan] - Add custom server\n"
230
+ content += " [cyan]/mcp list[/cyan] - View configured servers\n"
231
+ content += " [cyan]/mcp status[/cyan] - Check server health\n\n"
232
+ content += "[bold yellow]🌟 Popular MCP Servers[/bold yellow]\n\n"
233
+ content += "[green]@anthropic/mcp-server-github[/green]\n"
234
+ content += " GitHub integration\n\n"
235
+
236
+ content += "[green]@anthropic/mcp-server-postgres[/green]\n"
237
+ content += " Database access\n\n"
238
+ content += "[dim]Custom JSON configuration also supported[/dim]"
239
+
240
+ return content
241
+
242
+
243
+ def slide_appearance() -> str:
244
+ """Slide 4: Appearance customization.
245
+
246
+ Returns:
247
+ Rich markup string for the slide content.
248
+ """
249
+ content = "[bold cyan]🎨 Appearance[/bold cyan]\n\n"
250
+ content += "[white]Customize your Code Puppy experience![/white]\n\n"
251
+ content += "[green]Banner Colors:[/green]\n"
252
+ content += " [cyan]/colors[/cyan] - Customize tool banners\n"
253
+ content += " Change colors for THINKING, SHELL,\n"
254
+ content += " READ FILE, EDIT FILE, etc.\n\n"
255
+ content += "[green]Diff Highlighting:[/green]\n"
256
+ content += " [cyan]/diff[/cyan] - Syntax highlighting themes\n"
257
+ content += " Configure addition/deletion colors\n"
258
+ content += " with live preview!\n\n"
259
+ content += "[bold yellow]🖼️ Preview[/bold yellow]\n\n"
260
+ content += "[on blue] THINKING [/on blue] ⚡\n"
261
+ content += "[on dark_cyan] SHELL COMMAND [/on dark_cyan] 🚀\n"
262
+ content += "[on green] EDIT FILE [/on green] ✏️\n"
263
+ content += "[on purple] READ FILE [/on purple] 📂\n"
264
+ content += "[dim]All colors customizable![/dim]"
265
+
266
+ return content
267
+
268
+
269
+ def slide_agent_system() -> str:
270
+ """Slide 5: Agent system overview.
271
+
272
+ Returns:
273
+ Rich markup string for the slide content.
274
+ """
275
+ content = "[bold cyan]🤖 Agent System[/bold cyan]\n\n"
276
+ content += "[white]Code Puppy has specialized sub-agents![/white]\n\n"
277
+ content += "[green]Switch agents:[/green]\n"
278
+ content += " [cyan]/agent[/cyan] or [cyan]/a[/cyan]\n\n"
279
+ content += "[green]Key Agents:[/green]\n"
280
+ for _, name, desc in KEY_AGENTS:
281
+ content += f" {name}\n"
282
+ content += f" [dim]{desc}[/dim]\n"
283
+ content += "\n\n"
284
+ content += "[bold yellow]🔧 Agent Tips[/bold yellow]\n\n"
285
+ content += "[green]Create custom agents:[/green]\n"
286
+ content += "[cyan]/agent agent-creator[/cyan]\n"
287
+ content += "[dim]Agents are defined with JSON[/dim]\n"
288
+ content += "[dim]in ~/.code_puppy/agents/[/dim]\n\n"
289
+ content += "[green]Each agent has:[/green]\n"
290
+ content += " • Custom system prompt\n"
291
+ content += " • Selected tools\n"
292
+ content += " • Optional pinned model\n"
293
+
294
+ return content
295
+
296
+
297
+ def slide_model_pinning() -> str:
298
+ """Slide 6: Model pinning feature.
299
+
300
+ Returns:
301
+ Rich markup string for the slide content.
302
+ """
303
+ content = "[bold cyan]📌 Model Pinning[/bold cyan]\n\n"
304
+ content += "[white]Pin specific models to agents![/white]\n\n"
305
+ content += "[green]Command:[/green]\n"
306
+ content += " [cyan]/pin_model[/cyan]\n\n"
307
+ content += "[green]Example Multi-LLM Workflow:[/green]\n"
308
+ content += " [yellow]Planning:[/yellow] Kimi k2 (thinking)\n"
309
+ content += " [yellow]Implement:[/yellow] Claude Opus 4\n"
310
+ content += " [yellow]Testing:[/yellow] GLM 4.7\n"
311
+ content += " [yellow]Review:[/yellow] GPT-5.2-codex\n\n"
312
+ content += "[bold yellow]🎯 Why Pin Models?[/bold yellow]\n\n"
313
+ content += "Different models excel at\n"
314
+ content += "different tasks:\n\n"
315
+ content += "[green]Planning:[/green]\n"
316
+ content += " Deep reasoning models\n\n"
317
+ content += "[green]Coding:[/green]\n"
318
+ content += " Code-specialized models\n\n"
319
+ content += "[green]Review:[/green]\n"
320
+ content += " Critical analysis models\n\n"
321
+ content += "[dim]Mix and match for best results![/dim]"
322
+
323
+ return content
324
+
325
+
326
+ def slide_planning_agent() -> str:
327
+ """Slide 7: Planning agent power.
328
+
329
+ Returns:
330
+ Rich markup string for the slide content.
331
+ """
332
+ content = "[bold cyan]🚀 The Planning Agent[/bold cyan]\n\n"
333
+ content += "[bold yellow]Your Secret Weapon![/bold yellow]\n\n"
334
+ content += "The Planning Agent can:\n\n"
335
+ content += " ✓ Break down complex tasks\n"
336
+ content += " ✓ Create actionable roadmaps\n"
337
+ content += " ✓ Orchestrate sub-agents\n"
338
+ content += " ✓ Manage multi-step workflows\n\n"
339
+ content += "[green]Try it:[/green]\n"
340
+ content += " [cyan]/agent planning-agent[/cyan]\n"
341
+ content += "[dim]Then describe your project![/dim]\n\n"
342
+ content += "[bold yellow]💡 Example Prompt[/bold yellow]\n\n"
343
+ content += "[cyan]'Plan a REST API for a\n"
344
+ content += "todo app with auth,\n"
345
+ content += "database, and tests'[/cyan]\n\n"
346
+ content += "The Planning Agent will:\n\n"
347
+ content += "1. Analyze requirements\n"
348
+ content += "2. Break into phases\n"
349
+ content += "3. Create file structure\n"
350
+ content += "4. Delegate to specialists\n"
351
+ content += "5. Track progress\n"
352
+
353
+ return content
354
+
355
+
356
+ def slide_model_settings() -> str:
357
+ """Slide 8: Model settings.
358
+
359
+ Returns:
360
+ Rich markup string for the slide content.
361
+ """
362
+ content = "[bold cyan]⚙️ Model Settings[/bold cyan]\n\n"
363
+ content += "[white]Fine-tune model behavior![/white]\n\n"
364
+ content += "[green]Command:[/green]\n"
365
+ content += " [cyan]/model_settings[/cyan] or [cyan]/ms[/cyan]\n\n"
366
+ content += "[green]Configurable Parameters:[/green]\n"
367
+ content += " [yellow]temperature[/yellow] - Creativity (0.0-2.0)\n"
368
+ content += " [yellow]max_tokens[/yellow] - Response length\n"
369
+ content += " [yellow]top_p[/yellow] - Nucleus sampling\n"
370
+ content += " [yellow]presence_penalty[/yellow] - Topic diversity\n\n"
371
+ content += "[bold yellow]🎛️ Common Settings[/bold yellow]\n\n"
372
+ content += "[green]Precise code:[/green]\n"
373
+ content += " temperature: 0.0-0.3\n\n"
374
+ content += "[green]Creative writing:[/green]\n"
375
+ content += " temperature: 0.7-1.0\n\n"
376
+ content += "[green]Long outputs:[/green]\n"
377
+ content += " max_tokens: 4096+\n\n"
378
+ content += "[dim]Settings persist per-session[/dim]"
379
+
380
+ return content
381
+
382
+
383
+ def slide_complete(trigger_oauth: str | None) -> str:
384
+ """Slide 9: Completion.
385
+
386
+ Args:
387
+ trigger_oauth: OAuth provider that was selected ("chatgpt"/"claude"/None).
388
+
389
+ Returns:
390
+ Rich markup string for the slide content.
391
+ """
392
+ content = "[bold green]🎉 You're All Set![/bold green]\n\n"
393
+ content += "[white]Code Puppy is ready to help![/white]\n\n"
394
+ content += "[bold cyan]Quick Reference:[/bold cyan]\n\n"
395
+ content += " [cyan]/model[/cyan] - Switch models\n"
396
+ content += " [cyan]/agent[/cyan] - Switch agents\n"
397
+ content += " [cyan]/set[/cyan] - Configure settings\n"
398
+ content += " [cyan]/add_model[/cyan] - Browse 1500+ models\n"
399
+ content += " [cyan]/mcp[/cyan] - MCP server management\n"
400
+ content += " [cyan]/help[/cyan] - Full command list\n\n"
401
+ content += "[bold yellow]Press Enter to start coding! 🐶[/bold yellow]\n"
402
+ content += "\n"
403
+ content += "[bold yellow]🐾 Happy Coding![/bold yellow]\n\n"
404
+ content += "[dim]Remember:[/dim]\n"
405
+ content += "• Be specific in prompts\n"
406
+ content += "• Let agents read files first\n"
407
+ content += "• Use planning for complex tasks\n"
408
+ content += "• Check diffs before applying\n\n"
409
+ content += "[green]Community:[/green]\n"
410
+ content += " GitHub: code-puppy\n"
411
+ content += " Discord: code-puppy\n"
412
+
413
+ if trigger_oauth:
414
+ content += f"\n[bold cyan]Next: {trigger_oauth.title()} OAuth[/bold cyan]"
415
+
416
+ return content
@@ -0,0 +1,426 @@
1
+ """Interactive TUI onboarding wizard for first-time Code Puppy users.
2
+
3
+ 🐶 Welcome to Code Puppy! This wizard guides new users through initial setup,
4
+ model configuration, and feature discovery. Uses the same TUI patterns as
5
+ colors_menu.py and diff_menu.py for a consistent experience.
6
+
7
+ Usage:
8
+ from code_puppy.command_line.onboarding_wizard import (
9
+ should_show_onboarding,
10
+ run_onboarding_wizard,
11
+ )
12
+
13
+ if should_show_onboarding():
14
+ result = await run_onboarding_wizard()
15
+ # result: "chatgpt", "claude", "completed", "skipped", or None
16
+ """
17
+
18
+ import io
19
+ import os
20
+ import sys
21
+ import time
22
+ from typing import List, Optional, Tuple
23
+
24
+ from prompt_toolkit import Application
25
+ from prompt_toolkit.formatted_text import ANSI, FormattedText
26
+ from prompt_toolkit.key_binding import KeyBindings
27
+ from prompt_toolkit.layout import Layout, Window
28
+ from prompt_toolkit.layout.controls import FormattedTextControl
29
+ from prompt_toolkit.widgets import Frame
30
+ from rich.console import Console
31
+
32
+ from code_puppy.config import CONFIG_DIR
33
+
34
+ from .onboarding_slides import (
35
+ MODEL_OPTIONS,
36
+ slide_agent_system,
37
+ slide_api_keys,
38
+ slide_appearance,
39
+ slide_complete,
40
+ slide_mcp_servers,
41
+ slide_model_pinning,
42
+ slide_model_settings,
43
+ slide_model_subscription,
44
+ slide_planning_agent,
45
+ slide_welcome,
46
+ )
47
+
48
+ # ============================================================================
49
+ # State Tracking (like motd.py pattern)
50
+ # ============================================================================
51
+
52
+ ONBOARDING_COMPLETE_FILE = os.path.join(CONFIG_DIR, "onboarding_complete")
53
+
54
+
55
+ def has_completed_onboarding() -> bool:
56
+ """Check if the user has already completed onboarding.
57
+
58
+ Returns:
59
+ True if onboarding has been completed, False otherwise.
60
+ """
61
+ return os.path.exists(ONBOARDING_COMPLETE_FILE)
62
+
63
+
64
+ def mark_onboarding_complete() -> None:
65
+ """Mark onboarding as complete by creating the tracking file."""
66
+ os.makedirs(os.path.dirname(ONBOARDING_COMPLETE_FILE), exist_ok=True)
67
+ with open(ONBOARDING_COMPLETE_FILE, "w") as f:
68
+ f.write("completed\n")
69
+
70
+
71
+ def should_show_onboarding() -> bool:
72
+ """Determine if the onboarding wizard should be shown.
73
+
74
+ Returns:
75
+ True if onboarding should be shown, False otherwise.
76
+ """
77
+ return not has_completed_onboarding()
78
+
79
+
80
+ def reset_onboarding() -> None:
81
+ """Reset onboarding state (useful for testing or re-running wizard)."""
82
+ if os.path.exists(ONBOARDING_COMPLETE_FILE):
83
+ os.remove(ONBOARDING_COMPLETE_FILE)
84
+
85
+
86
+ # ============================================================================
87
+ # Onboarding Wizard Class
88
+ # ============================================================================
89
+
90
+
91
+ class OnboardingWizard:
92
+ """Interactive onboarding wizard with slide-based navigation.
93
+
94
+ Attributes:
95
+ current_slide: Index of the currently displayed slide (0-9)
96
+ selected_option: Index of selected option within current slide
97
+ trigger_oauth: OAuth provider to trigger after wizard ("chatgpt"/"claude")
98
+ model_choice: User's model subscription selection
99
+ """
100
+
101
+ TOTAL_SLIDES = 10
102
+
103
+ def __init__(self):
104
+ """Initialize the onboarding wizard state."""
105
+ self.current_slide = 0
106
+ self.selected_option = 0
107
+ self.trigger_oauth: Optional[str] = None
108
+ self.model_choice: Optional[str] = None
109
+ self.result: Optional[str] = None
110
+ self._should_exit = False
111
+
112
+ def get_progress_indicator(self) -> str:
113
+ """Generate progress dots showing current slide position.
114
+
115
+ Returns:
116
+ String like "● ○ ○ ○ ○ ○ ○ ○ ○ ○" for slide 0.
117
+ """
118
+ dots = []
119
+ for i in range(self.TOTAL_SLIDES):
120
+ if i == self.current_slide:
121
+ dots.append("●")
122
+ else:
123
+ dots.append("○")
124
+ return " ".join(dots)
125
+
126
+ def get_slide_content(self) -> str:
127
+ """Get combined content for the current slide.
128
+
129
+ Returns:
130
+ Rich markup string for the slide content.
131
+ """
132
+ if self.current_slide == 0:
133
+ return slide_welcome()
134
+ elif self.current_slide == 1:
135
+ options = self.get_options_for_slide()
136
+ return slide_model_subscription(self.selected_option, options)
137
+ elif self.current_slide == 2:
138
+ return slide_api_keys(self.model_choice)
139
+ elif self.current_slide == 3:
140
+ return slide_mcp_servers()
141
+ elif self.current_slide == 4:
142
+ return slide_appearance()
143
+ elif self.current_slide == 5:
144
+ return slide_agent_system()
145
+ elif self.current_slide == 6:
146
+ return slide_model_pinning()
147
+ elif self.current_slide == 7:
148
+ return slide_planning_agent()
149
+ elif self.current_slide == 8:
150
+ return slide_model_settings()
151
+ else: # slide 9
152
+ return slide_complete(self.trigger_oauth)
153
+
154
+ def get_options_for_slide(self) -> List[Tuple[str, str]]:
155
+ """Get selectable options for the current slide.
156
+
157
+ Returns:
158
+ List of (id, label) tuples for options, or empty list if no options.
159
+ """
160
+ if self.current_slide == 1: # Model subscription slide
161
+ return [(opt[0], opt[1]) for opt in MODEL_OPTIONS]
162
+ return []
163
+
164
+ def handle_option_select(self) -> None:
165
+ """Handle selection of the current option."""
166
+ if self.current_slide == 1: # Model subscription
167
+ options = self.get_options_for_slide()
168
+ if 0 <= self.selected_option < len(options):
169
+ choice_id = options[self.selected_option][0]
170
+ self.model_choice = choice_id
171
+
172
+ # Set OAuth trigger for ChatGPT/Claude
173
+ if choice_id == "chatgpt":
174
+ self.trigger_oauth = "chatgpt"
175
+ elif choice_id == "claude":
176
+ self.trigger_oauth = "claude"
177
+
178
+ def next_slide(self) -> bool:
179
+ """Move to the next slide.
180
+
181
+ Returns:
182
+ True if moved to next slide, False if at last slide.
183
+ """
184
+ if self.current_slide < self.TOTAL_SLIDES - 1:
185
+ self.current_slide += 1
186
+ self.selected_option = 0
187
+ return True
188
+ return False
189
+
190
+ def prev_slide(self) -> bool:
191
+ """Move to the previous slide.
192
+
193
+ Returns:
194
+ True if moved to previous slide, False if at first slide.
195
+ """
196
+ if self.current_slide > 0:
197
+ self.current_slide -= 1
198
+ self.selected_option = 0
199
+ return True
200
+ return False
201
+
202
+ def next_option(self) -> None:
203
+ """Move to the next option within the current slide."""
204
+ options = self.get_options_for_slide()
205
+ if options:
206
+ self.selected_option = (self.selected_option + 1) % len(options)
207
+
208
+ def prev_option(self) -> None:
209
+ """Move to the previous option within the current slide."""
210
+ options = self.get_options_for_slide()
211
+ if options:
212
+ self.selected_option = (self.selected_option - 1) % len(options)
213
+
214
+
215
+ # ============================================================================
216
+ # TUI Rendering Functions
217
+ # ============================================================================
218
+
219
+
220
+ def _get_slide_panel_content(wizard: OnboardingWizard) -> ANSI:
221
+ """Generate the centered slide content.
222
+
223
+ Args:
224
+ wizard: The OnboardingWizard instance.
225
+
226
+ Returns:
227
+ ANSI object with formatted slide content.
228
+ """
229
+ buffer = io.StringIO()
230
+ console = Console(
231
+ file=buffer,
232
+ force_terminal=True,
233
+ width=80,
234
+ legacy_windows=False,
235
+ color_system="truecolor",
236
+ no_color=False,
237
+ force_interactive=True,
238
+ )
239
+
240
+ # Progress indicator
241
+ progress = wizard.get_progress_indicator()
242
+ console.print(f"[dim]{progress}[/dim]\n")
243
+
244
+ # Slide number
245
+ console.print(
246
+ f"[dim]Slide {wizard.current_slide + 1} of {wizard.TOTAL_SLIDES}[/dim]\n\n"
247
+ )
248
+
249
+ # Combined slide content
250
+ slide_content = wizard.get_slide_content()
251
+ console.print(slide_content)
252
+
253
+ return ANSI(buffer.getvalue())
254
+
255
+
256
+ def _get_navigation_hints(wizard: OnboardingWizard) -> FormattedText:
257
+ """Generate navigation hints for the bottom of the screen.
258
+
259
+ Args:
260
+ wizard: The OnboardingWizard instance.
261
+
262
+ Returns:
263
+ FormattedText with navigation hints.
264
+ """
265
+ hints = []
266
+
267
+ if wizard.current_slide > 0:
268
+ hints.append(("fg:ansicyan", "← Back "))
269
+
270
+ if wizard.current_slide < wizard.TOTAL_SLIDES - 1:
271
+ hints.append(("fg:ansicyan", "→ Next "))
272
+ else:
273
+ hints.append(("fg:ansigreen bold", "Enter: Finish "))
274
+
275
+ options = wizard.get_options_for_slide()
276
+ if options:
277
+ hints.append(("fg:ansicyan", "↑↓ Options "))
278
+
279
+ hints.append(("fg:ansiyellow", "ESC: Skip"))
280
+
281
+ return FormattedText(hints)
282
+
283
+
284
+ # ============================================================================
285
+ # Main Entry Point
286
+ # ============================================================================
287
+
288
+
289
+ async def run_onboarding_wizard() -> Optional[str]:
290
+ """Run the interactive onboarding wizard.
291
+
292
+ Returns:
293
+ - "chatgpt" if user wants ChatGPT OAuth
294
+ - "claude" if user wants Claude OAuth
295
+ - "completed" if finished normally
296
+ - "skipped" if user pressed ESC
297
+ - None on error
298
+ """
299
+ from code_puppy.tools.command_runner import set_awaiting_user_input
300
+
301
+ wizard = OnboardingWizard()
302
+
303
+ set_awaiting_user_input(True)
304
+
305
+ # Enter alternate screen buffer
306
+ sys.stdout.write("\033[?1049h") # Enter alternate buffer
307
+ sys.stdout.write("\033[2J\033[H") # Clear and home
308
+ sys.stdout.flush()
309
+ time.sleep(0.1) # Minimal delay for state sync
310
+
311
+ try:
312
+ # Set up key bindings
313
+ kb = KeyBindings()
314
+
315
+ @kb.add("right")
316
+ @kb.add("l")
317
+ def next_slide(event):
318
+ if wizard.current_slide == wizard.TOTAL_SLIDES - 1:
319
+ # On last slide, right arrow finishes
320
+ wizard.result = "completed"
321
+ wizard._should_exit = True
322
+ event.app.exit()
323
+ else:
324
+ wizard.next_slide()
325
+ event.app.invalidate()
326
+
327
+ @kb.add("left")
328
+ @kb.add("h")
329
+ def prev_slide(event):
330
+ wizard.prev_slide()
331
+ event.app.invalidate()
332
+
333
+ @kb.add("down")
334
+ @kb.add("j")
335
+ def next_option(event):
336
+ wizard.next_option()
337
+ event.app.invalidate()
338
+
339
+ @kb.add("up")
340
+ @kb.add("k")
341
+ def prev_option(event):
342
+ wizard.prev_option()
343
+ event.app.invalidate()
344
+
345
+ @kb.add("enter")
346
+ def select_or_next(event):
347
+ # Handle option selection on slides with options
348
+ options = wizard.get_options_for_slide()
349
+ if options:
350
+ wizard.handle_option_select()
351
+
352
+ # Move to next slide or finish
353
+ if wizard.current_slide == wizard.TOTAL_SLIDES - 1:
354
+ wizard.result = "completed"
355
+ wizard._should_exit = True
356
+ event.app.exit()
357
+ else:
358
+ wizard.next_slide()
359
+ event.app.invalidate()
360
+
361
+ @kb.add("escape")
362
+ def skip_wizard(event):
363
+ wizard.result = "skipped"
364
+ wizard._should_exit = True
365
+ event.app.exit()
366
+
367
+ @kb.add("c-c")
368
+ def cancel_wizard(event):
369
+ wizard.result = "skipped"
370
+ wizard._should_exit = True
371
+ event.app.exit()
372
+
373
+ # Create layout with single centered panel
374
+ slide_panel = Window(
375
+ content=FormattedTextControl(lambda: _get_slide_panel_content(wizard))
376
+ )
377
+
378
+ root_container = Frame(slide_panel, title="🐶 Welcome to Code Puppy!")
379
+
380
+ layout = Layout(root_container)
381
+
382
+ app = Application(
383
+ layout=layout,
384
+ key_bindings=kb,
385
+ full_screen=False,
386
+ mouse_support=False,
387
+ color_depth="DEPTH_24_BIT",
388
+ )
389
+
390
+ # Clear screen before running
391
+ sys.stdout.write("\033[2J\033[H")
392
+ sys.stdout.flush()
393
+
394
+ # Run the application
395
+ await app.run_async()
396
+
397
+ except KeyboardInterrupt:
398
+ wizard.result = "skipped"
399
+ except Exception:
400
+ wizard.result = None
401
+ finally:
402
+ set_awaiting_user_input(False)
403
+ # Exit alternate screen buffer
404
+ sys.stdout.write("\033[?1049l")
405
+ sys.stdout.flush()
406
+
407
+ # Mark onboarding as complete (even if skipped - they saw it)
408
+ if wizard.result in ("completed", "skipped"):
409
+ mark_onboarding_complete()
410
+
411
+ # Return OAuth trigger if selected, otherwise the result
412
+ if wizard.trigger_oauth:
413
+ return wizard.trigger_oauth
414
+
415
+ return wizard.result
416
+
417
+
418
+ async def run_onboarding_if_needed() -> Optional[str]:
419
+ """Run onboarding wizard if user hasn't completed it yet.
420
+
421
+ Returns:
422
+ Result from run_onboarding_wizard() or None if not needed.
423
+ """
424
+ if should_show_onboarding():
425
+ return await run_onboarding_wizard()
426
+ return None
code_puppy/config.py CHANGED
@@ -1051,11 +1051,11 @@ def set_enable_dbos(enabled: bool) -> None:
1051
1051
  set_config_value("enable_dbos", "true" if enabled else "false")
1052
1052
 
1053
1053
 
1054
- def get_message_limit(default: int = 100) -> int:
1054
+ def get_message_limit(default: int = 1000) -> int:
1055
1055
  """
1056
1056
  Returns the user-configured message/request limit for the agent.
1057
1057
  This controls how many steps/requests the agent can take.
1058
- Defaults to 100 if unset or misconfigured.
1058
+ Defaults to 1000 if unset or misconfigured.
1059
1059
  Configurable by 'message_limit' key.
1060
1060
  """
1061
1061
  val = get_value("message_limit")
@@ -6,6 +6,7 @@ import os
6
6
  from typing import List, Optional, Tuple
7
7
 
8
8
  from code_puppy.callbacks import register_callback
9
+ from code_puppy.config import set_model_name
9
10
  from code_puppy.messaging import emit_info, emit_success, emit_warning
10
11
 
11
12
  from .config import CHATGPT_OAUTH_CONFIG, get_token_storage_path
@@ -75,6 +76,7 @@ def _handle_custom_command(command: str, name: str) -> Optional[bool]:
75
76
 
76
77
  if name == "chatgpt-auth":
77
78
  run_oauth_flow()
79
+ set_model_name("chatgpt-gpt-5.2-codex")
78
80
  return True
79
81
 
80
82
  if name == "chatgpt-status":
@@ -12,6 +12,7 @@ from typing import Any, Dict, List, Optional, Tuple
12
12
  from urllib.parse import parse_qs, urlparse
13
13
 
14
14
  from code_puppy.callbacks import register_callback
15
+ from code_puppy.config import set_model_name
15
16
  from code_puppy.messaging import emit_error, emit_info, emit_success, emit_warning
16
17
 
17
18
  from ..oauth_puppy_html import oauth_failure_html, oauth_success_html
@@ -260,6 +261,7 @@ def _handle_custom_command(command: str, name: str) -> Optional[bool]:
260
261
  "Existing Claude Code tokens found. Continuing will overwrite them."
261
262
  )
262
263
  _perform_authentication()
264
+ set_model_name("claude-code-claude-opus-4-5-20251101")
263
265
  return True
264
266
 
265
267
  if name == "claude-code-status":
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code-puppy
3
- Version: 0.0.333
3
+ Version: 0.0.334
4
4
  Summary: Code generation agent
5
5
  Project-URL: repository, https://github.com/mpfaffenberger/code_puppy
6
6
  Project-URL: HomePage, https://github.com/mpfaffenberger/code_puppy
@@ -3,8 +3,8 @@ code_puppy/__main__.py,sha256=pDVssJOWP8A83iFkxMLY9YteHYat0EyWDQqMkKHpWp4,203
3
3
  code_puppy/callbacks.py,sha256=hqTV--dNxG5vwWWm3MrEjmb8MZuHFFdmHePl23NXPHk,8621
4
4
  code_puppy/chatgpt_codex_client.py,sha256=Om0ANB_kpHubhCwNzF9ENf8RvKBqs0IYzBLl_SNw0Vk,9833
5
5
  code_puppy/claude_cache_client.py,sha256=hZr_YtXZSQvBoJFtRbbecKucYqJgoMopqUmm0IxFYGY,6071
6
- code_puppy/cli_runner.py,sha256=VkDaANmywBNoj6wCzaLlI1Z2yFC98U4BqHmSIcV4HCw,32150
7
- code_puppy/config.py,sha256=qqeJrQP7gqADqeYqVzfksP7NYGROLrBQCuYic5PuQfY,52295
6
+ code_puppy/cli_runner.py,sha256=_nbaVSsX5FfXEf0jSiVQ7cPUZ96JUFYV71bD668YfR4,32216
7
+ code_puppy/config.py,sha256=25HJ6ppNIPUWg5uHMIBrF2wY-lyqqaDVSpBfghFik3g,52297
8
8
  code_puppy/error_logging.py,sha256=a80OILCUtJhexI6a9GM-r5LqIdjvSRzggfgPp2jv1X0,3297
9
9
  code_puppy/gemini_code_assist.py,sha256=KGS7sO5OLc83nDF3xxS-QiU6vxW9vcm6hmzilu79Ef8,13867
10
10
  code_puppy/http_utils.py,sha256=w5mWYIGIWJZJvgvMahXs9BmdidoJvGn4CASDRY88a8o,13414
@@ -50,8 +50,8 @@ code_puppy/command_line/autosave_menu.py,sha256=7w2SXfEfR-SGFZcHxM-QZfT0p42KxJjX
50
50
  code_puppy/command_line/colors_menu.py,sha256=F_OYuApwXWGP2w9o0CMEbIHtqwdKUh5eDhi7qtDP9h0,17144
51
51
  code_puppy/command_line/command_handler.py,sha256=CY9F27eovZJK_kpU1YmbroYLWGTCuouCOQ-TXfDp-nw,10916
52
52
  code_puppy/command_line/command_registry.py,sha256=qFySsw1g8dol3kgi0p6cXrIDlP11_OhOoaQ5nAadWXg,4416
53
- code_puppy/command_line/config_commands.py,sha256=5RG8oBaG52vBbZ2Nd5BnHszh_uwPit23AYmEckJRyTc,25764
54
- code_puppy/command_line/core_commands.py,sha256=qxIZ5tCAPkNDfDYjcG86h0tsi7RUQ4ueQ5C2BZ2H214,24809
53
+ code_puppy/command_line/config_commands.py,sha256=qS9Cm758DPz2QGvHLhAV4Tp_Xfgo3PyoCoLDusbnmCw,25742
54
+ code_puppy/command_line/core_commands.py,sha256=MhnyqcTIMheT8W1cqmfZTGLWo8iGM-fAAo8E3dNORro,26468
55
55
  code_puppy/command_line/diff_menu.py,sha256=6qolM8ECpXTAo2q0Yvqw8Oohsj3MLPxQI8PJvYuKGS4,24014
56
56
  code_puppy/command_line/file_path_completion.py,sha256=gw8NpIxa6GOpczUJRyh7VNZwoXKKn-yvCqit7h2y6Gg,2931
57
57
  code_puppy/command_line/load_context_completion.py,sha256=a3JvLDeLLSYxVgTjAdqWzS4spjv6ccCrK2LKZgVJ1IM,2202
@@ -59,6 +59,8 @@ code_puppy/command_line/mcp_completion.py,sha256=eKzW2O7gun7HoHekOW0XVXhNS5J2xCt
59
59
  code_puppy/command_line/model_picker_completion.py,sha256=nDnlf0qFCG2zAm_mWW2eMYwVC7eROVQrFe92hZqOKa8,6810
60
60
  code_puppy/command_line/model_settings_menu.py,sha256=O5nPp_OyShFcXzpSmsCeYsnnVNrSwcTBFY9bzcayvj0,32263
61
61
  code_puppy/command_line/motd.py,sha256=OoNxwewsckexSgJ5H5y40IawP-TzqlqY-rqFUdRbIhs,2186
62
+ code_puppy/command_line/onboarding_slides.py,sha256=EdssKWx_hT3mneeMEL4QXTLiUxdm-ZXfb6QJOSFtBbE,17066
63
+ code_puppy/command_line/onboarding_wizard.py,sha256=hoMvSb3zvHGSScaCqPMaXSjSAvfX_OMUz7Hu68wP2qE,13134
62
64
  code_puppy/command_line/pin_command_completion.py,sha256=juSvdqRpk7AdfkPy1DJx5NzfEUU5KYGlChvP0hisM18,11667
63
65
  code_puppy/command_line/prompt_toolkit_completion.py,sha256=x4Of32g8oH9ckhx-P6BigV7HUUhhjL8xkvK03uq9HRw,27308
64
66
  code_puppy/command_line/session_commands.py,sha256=Jh8GGfhlfBAEVfucKLbcZjNaXYd0twImiOwq2ZnGdQQ,9902
@@ -122,14 +124,14 @@ code_puppy/plugins/oauth_puppy_html.py,sha256=Wpa-V_NlRiBAvo_OXHuR7wvOH_jSt8L9HS
122
124
  code_puppy/plugins/chatgpt_oauth/__init__.py,sha256=Kjc6Hsz1sWvMD2OdAlWZvJRiKJSj4fx22boa-aVFKjA,189
123
125
  code_puppy/plugins/chatgpt_oauth/config.py,sha256=H_wAH9Duyn8WH2Kq8oe72uda-_4qu1uXLPun_SDdtsk,2023
124
126
  code_puppy/plugins/chatgpt_oauth/oauth_flow.py,sha256=i-CP2gpzEBT3ogUt-oTMexiP2on41N6PbRGIy2lZF30,11028
125
- code_puppy/plugins/chatgpt_oauth/register_callbacks.py,sha256=-JB1isODQ7v2Kn2alnSIg3mPjqnC-wTfsMO_G12V7iU,2872
127
+ code_puppy/plugins/chatgpt_oauth/register_callbacks.py,sha256=oPfAOdh5hp3Jg3tK5ylk1sy0ydxBebK9a9w1EL1dw9I,2965
126
128
  code_puppy/plugins/chatgpt_oauth/test_plugin.py,sha256=oHX7Eb_Hb4rgRpOWdhtFp8Jj6_FDuvXQITRPiNy4tRo,9622
127
129
  code_puppy/plugins/chatgpt_oauth/utils.py,sha256=fzpsCQOv0kqPWmG5vNEV_GLSUrMQh8cF7tdIjSOt1Dc,16504
128
130
  code_puppy/plugins/claude_code_oauth/README.md,sha256=76nHhMlhk61DZa5g0Q2fc0AtpplLmpbwuWFZt7PHH5g,5458
129
131
  code_puppy/plugins/claude_code_oauth/SETUP.md,sha256=lnGzofPLogBy3oPPFLv5_cZ7vjg_GYrIyYnF-EoTJKg,3278
130
132
  code_puppy/plugins/claude_code_oauth/__init__.py,sha256=mCcOU-wM7LNCDjr-w-WLPzom8nTF1UNt4nqxGE6Rt0k,187
131
133
  code_puppy/plugins/claude_code_oauth/config.py,sha256=DjGySCkvjSGZds6DYErLMAi3TItt8iSLGvyJN98nSEM,2013
132
- code_puppy/plugins/claude_code_oauth/register_callbacks.py,sha256=0NeX1hhkYIlVfPmjZ1xmcf1yueDAJh_FMUmvJlxSO-E,10057
134
+ code_puppy/plugins/claude_code_oauth/register_callbacks.py,sha256=g8sl-i7jIOF6OFALeaLqTF3mS4tD8GR_FCzvPjVw2js,10165
133
135
  code_puppy/plugins/claude_code_oauth/test_plugin.py,sha256=yQy4EeZl4bjrcog1d8BjknoDTRK75mRXXvkSQJYSSEM,9286
134
136
  code_puppy/plugins/claude_code_oauth/utils.py,sha256=wDaOU21zB3y6PWkuMXwE4mFjQuffyDae-vXysPTS-w8,13438
135
137
  code_puppy/plugins/customizable_commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -160,10 +162,10 @@ code_puppy/tools/browser/browser_scripts.py,sha256=sNb8eLEyzhasy5hV4B9OjM8yIVMLV
160
162
  code_puppy/tools/browser/browser_workflows.py,sha256=nitW42vCf0ieTX1gLabozTugNQ8phtoFzZbiAhw1V90,6491
161
163
  code_puppy/tools/browser/camoufox_manager.py,sha256=RZjGOEftE5sI_tsercUyXFSZI2wpStXf-q0PdYh2G3I,8680
162
164
  code_puppy/tools/browser/vqa_agent.py,sha256=DBn9HKloILqJSTSdNZzH_PYWT0B2h9VwmY6akFQI_uU,2913
163
- code_puppy-0.0.333.data/data/code_puppy/models.json,sha256=IPABdOrDw2OZJxa0XGBWSWmBRerV6_pIEmKVLRtUbAk,3105
164
- code_puppy-0.0.333.data/data/code_puppy/models_dev_api.json,sha256=wHjkj-IM_fx1oHki6-GqtOoCrRMR0ScK0f-Iz0UEcy8,548187
165
- code_puppy-0.0.333.dist-info/METADATA,sha256=VP7p42x7DQGz8JDM0nIF7MXuY41YkD0wIxBINx4334w,28854
166
- code_puppy-0.0.333.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
167
- code_puppy-0.0.333.dist-info/entry_points.txt,sha256=Tp4eQC99WY3HOKd3sdvb22vZODRq0XkZVNpXOag_KdI,91
168
- code_puppy-0.0.333.dist-info/licenses/LICENSE,sha256=31u8x0SPgdOq3izJX41kgFazWsM43zPEF9eskzqbJMY,1075
169
- code_puppy-0.0.333.dist-info/RECORD,,
165
+ code_puppy-0.0.334.data/data/code_puppy/models.json,sha256=IPABdOrDw2OZJxa0XGBWSWmBRerV6_pIEmKVLRtUbAk,3105
166
+ code_puppy-0.0.334.data/data/code_puppy/models_dev_api.json,sha256=wHjkj-IM_fx1oHki6-GqtOoCrRMR0ScK0f-Iz0UEcy8,548187
167
+ code_puppy-0.0.334.dist-info/METADATA,sha256=DhyliNW_DINXn1BNLHXz80YoI7JrvOltgzhFP4LA77I,28854
168
+ code_puppy-0.0.334.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
169
+ code_puppy-0.0.334.dist-info/entry_points.txt,sha256=Tp4eQC99WY3HOKd3sdvb22vZODRq0XkZVNpXOag_KdI,91
170
+ code_puppy-0.0.334.dist-info/licenses/LICENSE,sha256=31u8x0SPgdOq3izJX41kgFazWsM43zPEF9eskzqbJMY,1075
171
+ code_puppy-0.0.334.dist-info/RECORD,,