connectonion 0.5.8__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.
Files changed (113) hide show
  1. connectonion/__init__.py +78 -0
  2. connectonion/address.py +320 -0
  3. connectonion/agent.py +450 -0
  4. connectonion/announce.py +84 -0
  5. connectonion/asgi.py +287 -0
  6. connectonion/auto_debug_exception.py +181 -0
  7. connectonion/cli/__init__.py +3 -0
  8. connectonion/cli/browser_agent/__init__.py +5 -0
  9. connectonion/cli/browser_agent/browser.py +243 -0
  10. connectonion/cli/browser_agent/prompt.md +107 -0
  11. connectonion/cli/commands/__init__.py +1 -0
  12. connectonion/cli/commands/auth_commands.py +527 -0
  13. connectonion/cli/commands/browser_commands.py +27 -0
  14. connectonion/cli/commands/create.py +511 -0
  15. connectonion/cli/commands/deploy_commands.py +220 -0
  16. connectonion/cli/commands/doctor_commands.py +173 -0
  17. connectonion/cli/commands/init.py +469 -0
  18. connectonion/cli/commands/project_cmd_lib.py +828 -0
  19. connectonion/cli/commands/reset_commands.py +149 -0
  20. connectonion/cli/commands/status_commands.py +168 -0
  21. connectonion/cli/docs/co-vibecoding-principles-docs-contexts-all-in-one.md +2010 -0
  22. connectonion/cli/docs/connectonion.md +1256 -0
  23. connectonion/cli/docs.md +123 -0
  24. connectonion/cli/main.py +148 -0
  25. connectonion/cli/templates/meta-agent/README.md +287 -0
  26. connectonion/cli/templates/meta-agent/agent.py +196 -0
  27. connectonion/cli/templates/meta-agent/prompts/answer_prompt.md +9 -0
  28. connectonion/cli/templates/meta-agent/prompts/docs_retrieve_prompt.md +15 -0
  29. connectonion/cli/templates/meta-agent/prompts/metagent.md +71 -0
  30. connectonion/cli/templates/meta-agent/prompts/think_prompt.md +18 -0
  31. connectonion/cli/templates/minimal/README.md +56 -0
  32. connectonion/cli/templates/minimal/agent.py +40 -0
  33. connectonion/cli/templates/playwright/README.md +118 -0
  34. connectonion/cli/templates/playwright/agent.py +336 -0
  35. connectonion/cli/templates/playwright/prompt.md +102 -0
  36. connectonion/cli/templates/playwright/requirements.txt +3 -0
  37. connectonion/cli/templates/web-research/agent.py +122 -0
  38. connectonion/connect.py +128 -0
  39. connectonion/console.py +539 -0
  40. connectonion/debug_agent/__init__.py +13 -0
  41. connectonion/debug_agent/agent.py +45 -0
  42. connectonion/debug_agent/prompts/debug_assistant.md +72 -0
  43. connectonion/debug_agent/runtime_inspector.py +406 -0
  44. connectonion/debug_explainer/__init__.py +10 -0
  45. connectonion/debug_explainer/explain_agent.py +114 -0
  46. connectonion/debug_explainer/explain_context.py +263 -0
  47. connectonion/debug_explainer/explainer_prompt.md +29 -0
  48. connectonion/debug_explainer/root_cause_analysis_prompt.md +43 -0
  49. connectonion/debugger_ui.py +1039 -0
  50. connectonion/decorators.py +208 -0
  51. connectonion/events.py +248 -0
  52. connectonion/execution_analyzer/__init__.py +9 -0
  53. connectonion/execution_analyzer/execution_analysis.py +93 -0
  54. connectonion/execution_analyzer/execution_analysis_prompt.md +47 -0
  55. connectonion/host.py +579 -0
  56. connectonion/interactive_debugger.py +342 -0
  57. connectonion/llm.py +801 -0
  58. connectonion/llm_do.py +307 -0
  59. connectonion/logger.py +300 -0
  60. connectonion/prompt_files/__init__.py +1 -0
  61. connectonion/prompt_files/analyze_contact.md +62 -0
  62. connectonion/prompt_files/eval_expected.md +12 -0
  63. connectonion/prompt_files/react_evaluate.md +11 -0
  64. connectonion/prompt_files/react_plan.md +16 -0
  65. connectonion/prompt_files/reflect.md +22 -0
  66. connectonion/prompts.py +144 -0
  67. connectonion/relay.py +200 -0
  68. connectonion/static/docs.html +688 -0
  69. connectonion/tool_executor.py +279 -0
  70. connectonion/tool_factory.py +186 -0
  71. connectonion/tool_registry.py +105 -0
  72. connectonion/trust.py +166 -0
  73. connectonion/trust_agents.py +71 -0
  74. connectonion/trust_functions.py +88 -0
  75. connectonion/tui/__init__.py +57 -0
  76. connectonion/tui/divider.py +39 -0
  77. connectonion/tui/dropdown.py +251 -0
  78. connectonion/tui/footer.py +31 -0
  79. connectonion/tui/fuzzy.py +56 -0
  80. connectonion/tui/input.py +278 -0
  81. connectonion/tui/keys.py +35 -0
  82. connectonion/tui/pick.py +130 -0
  83. connectonion/tui/providers.py +155 -0
  84. connectonion/tui/status_bar.py +163 -0
  85. connectonion/usage.py +161 -0
  86. connectonion/useful_events_handlers/__init__.py +16 -0
  87. connectonion/useful_events_handlers/reflect.py +116 -0
  88. connectonion/useful_plugins/__init__.py +20 -0
  89. connectonion/useful_plugins/calendar_plugin.py +163 -0
  90. connectonion/useful_plugins/eval.py +139 -0
  91. connectonion/useful_plugins/gmail_plugin.py +162 -0
  92. connectonion/useful_plugins/image_result_formatter.py +127 -0
  93. connectonion/useful_plugins/re_act.py +78 -0
  94. connectonion/useful_plugins/shell_approval.py +159 -0
  95. connectonion/useful_tools/__init__.py +44 -0
  96. connectonion/useful_tools/diff_writer.py +192 -0
  97. connectonion/useful_tools/get_emails.py +183 -0
  98. connectonion/useful_tools/gmail.py +1596 -0
  99. connectonion/useful_tools/google_calendar.py +613 -0
  100. connectonion/useful_tools/memory.py +380 -0
  101. connectonion/useful_tools/microsoft_calendar.py +604 -0
  102. connectonion/useful_tools/outlook.py +488 -0
  103. connectonion/useful_tools/send_email.py +205 -0
  104. connectonion/useful_tools/shell.py +97 -0
  105. connectonion/useful_tools/slash_command.py +201 -0
  106. connectonion/useful_tools/terminal.py +285 -0
  107. connectonion/useful_tools/todo_list.py +241 -0
  108. connectonion/useful_tools/web_fetch.py +216 -0
  109. connectonion/xray.py +467 -0
  110. connectonion-0.5.8.dist-info/METADATA +741 -0
  111. connectonion-0.5.8.dist-info/RECORD +113 -0
  112. connectonion-0.5.8.dist-info/WHEEL +4 -0
  113. connectonion-0.5.8.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,469 @@
1
+ """
2
+ Purpose: Initialize ConnectOnion project in current directory with template files, authentication, and configuration
3
+ LLM-Note:
4
+ Dependencies: imports from [os, sys, shutil, subprocess, toml, datetime, pathlib, rich.console, rich.prompt, __version__, address, auth_commands.authenticate, project_cmd_lib] | imported by [cli/main.py via handle_init()] | uses templates from [cli/templates/{minimal,playwright}] | tested by [tests/cli/test_cli_init.py]
5
+ Data flow: receives args (ai, key, template, description, yes, force) from CLI parser → ensure_global_config() creates ~/.co/ with master keypair if needed → check_environment_for_api_keys() detects existing keys → api_key_setup_menu() or detect_api_provider() validates API key → generate_custom_template() if template='custom' → copy template files from cli/templates/{template}/ to current dir → authenticate() to get OPENONION_API_KEY → create/update .env with API keys from ~/.co/keys.env → create .co/config.toml with project metadata and global identity → copy vibe coding docs to .co/docs/ and project root → update .gitignore if git repo → display success message with next steps
6
+ State/Effects: modifies ~/.co/ (config.toml, keys.env, keys/, logs/) on first run | writes to current dir: .co/config.toml, .env, agent.py (if template), .gitignore, co-vibecoding-principles-docs-contexts-all-in-one.md | calls authenticate() which writes OPENONION_API_KEY to ~/.co/keys.env | copies template files (agent.py, requirements.txt, etc.) | creates temp_project_dir during auth flow (cleaned up at end) | writes to stdout via rich.Console
7
+ Integration: exposes handle_init(ai, key, template, description, yes, force) | calls ensure_global_config() to create global identity | calls authenticate(global_co_dir, save_to_project=False) for managed keys | uses template files from cli/templates/ | relies on project_cmd_lib for shared functions | uses address.generate() and address.save() for Ed25519 keypair | template options: 'minimal', 'playwright', 'custom', 'none' (default)
8
+ Performance: authenticate() makes network call to backend (2-5s) | generate_custom_template() calls LLM API if template='custom' | template file copying is O(n) files | config/env file operations are I/O bound
9
+ Errors: fails if cli/templates/{template}/ not found | fails if API key invalid during authenticate() | warns if directory not empty (requires --force or confirmation) | warns for special directories (home, root, system dirs) | skips duplicate .env keys (safe append) | creates temp_project_dir but cleans up on completion
10
+ """
11
+
12
+ import os
13
+ import sys
14
+ import shutil
15
+ import subprocess
16
+ import toml
17
+ from datetime import datetime
18
+ from pathlib import Path
19
+ from typing import Optional, Dict, Any
20
+ from rich.console import Console
21
+ from rich.prompt import Prompt, Confirm
22
+ from rich.syntax import Syntax
23
+ from rich.text import Text
24
+
25
+ from ... import __version__
26
+ from ... import address
27
+ from .auth_commands import authenticate
28
+
29
+ # Import shared functions from project_cmd_lib
30
+ from .project_cmd_lib import (
31
+ get_special_directory_warning,
32
+ is_directory_empty,
33
+ check_environment_for_api_keys,
34
+ api_key_setup_menu,
35
+ detect_api_provider,
36
+ get_template_info,
37
+ interactive_menu,
38
+ generate_custom_template,
39
+ show_progress,
40
+ configure_env_for_provider
41
+ )
42
+
43
+ console = Console()
44
+
45
+
46
+ def ensure_global_config() -> Dict[str, Any]:
47
+ """Simple function to ensure ~/.co/ exists with global identity."""
48
+ global_dir = Path.home() / ".co"
49
+ config_path = global_dir / "config.toml"
50
+
51
+ # If exists, just load and return
52
+ if config_path.exists():
53
+ with open(config_path, 'r', encoding='utf-8') as f:
54
+ return toml.load(f)
55
+
56
+ # First time - create global config
57
+ console.print(f"\n🚀 Welcome to ConnectOnion!")
58
+ console.print(f"✨ Setting up global configuration...")
59
+
60
+ # Create directories
61
+ global_dir.mkdir(exist_ok=True)
62
+ (global_dir / "keys").mkdir(exist_ok=True)
63
+ (global_dir / "logs").mkdir(exist_ok=True)
64
+
65
+ # Generate master keys - fail fast if libraries missing
66
+ addr_data = address.generate()
67
+ address.save(addr_data, global_dir)
68
+ console.print(f" ✓ Generated master keypair")
69
+ console.print(f" ✓ Your address: {addr_data['short_address']}")
70
+
71
+ # Create config (simplified - address/email now in .env)
72
+ config = {
73
+ "connectonion": {
74
+ "framework_version": __version__,
75
+ "created": datetime.now().isoformat(),
76
+ },
77
+ "cli": {
78
+ "version": "1.0.0",
79
+ },
80
+ "agent": {
81
+ "algorithm": "ed25519",
82
+ "default_model": "co/gemini-2.5-pro",
83
+ "max_iterations": 10,
84
+ "created_at": datetime.now().isoformat(),
85
+ },
86
+ }
87
+
88
+ # Save config
89
+ with open(config_path, 'w', encoding='utf-8') as f:
90
+ toml.dump(config, f)
91
+ console.print(f" ✓ Created ~/.co/config.toml")
92
+
93
+ # Create keys.env with config path and agent address
94
+ keys_env = global_dir / "keys.env"
95
+ if not keys_env.exists():
96
+ with open(keys_env, 'w', encoding='utf-8') as f:
97
+ f.write(f"AGENT_CONFIG_PATH={global_dir}\n")
98
+ f.write(f"AGENT_ADDRESS={addr_data['address']}\n")
99
+ f.write("# Your agent address (Ed25519 public key) is used for:\n")
100
+ f.write("# - Secure agent communication (encrypt/decrypt with private key)\n")
101
+ f.write("# - Authentication with OpenOnion managed LLM provider\n")
102
+ f.write(f"# - Email address: {addr_data['address'][:10]}@mail.openonion.ai\n")
103
+ if sys.platform != 'win32':
104
+ os.chmod(keys_env, 0o600) # Read/write for owner only (Unix/Mac only)
105
+ else:
106
+ # Append if not exists
107
+ existing = keys_env.read_text()
108
+ if 'AGENT_CONFIG_PATH=' not in existing:
109
+ with open(keys_env, 'a', encoding='utf-8') as f:
110
+ f.write(f"AGENT_CONFIG_PATH={global_dir}\n")
111
+ if 'AGENT_ADDRESS=' not in existing:
112
+ with open(keys_env, 'a', encoding='utf-8') as f:
113
+ f.write(f"AGENT_ADDRESS={addr_data['address']}\n")
114
+ console.print(f" ✓ Created ~/.co/keys.env")
115
+
116
+ return config
117
+
118
+
119
+ def handle_init(ai: Optional[bool], key: Optional[str], template: Optional[str],
120
+ description: Optional[str], yes: bool, force: bool):
121
+ """Initialize a ConnectOnion project in the current directory."""
122
+ # Ensure global config exists first
123
+ global_config = ensure_global_config()
124
+ global_identity = global_config.get("agent", {})
125
+
126
+ current_dir = os.getcwd()
127
+ project_name = os.path.basename(current_dir) or "my-agent"
128
+
129
+ # Track temp directory for cleanup
130
+ temp_project_dir = None
131
+
132
+ # Header removed for cleaner output
133
+
134
+ # Check for special directories
135
+ warning = get_special_directory_warning(current_dir)
136
+ if warning:
137
+ console.print(f"[yellow]{warning}[/yellow]")
138
+ if not yes and not Confirm.ask("[yellow]Continue anyway?[/yellow]"):
139
+ console.print("[yellow]Initialization cancelled.[/yellow]")
140
+ return
141
+
142
+ # Check if directory is empty
143
+ if not is_directory_empty(current_dir) and not force:
144
+ existing_files = os.listdir(current_dir)[:5]
145
+ console.print("[yellow]⚠️ Directory not empty[/yellow]")
146
+ console.print(f"[yellow]Existing files: {', '.join(existing_files[:5])}[/yellow]")
147
+ if not yes and not Confirm.ask("\n[yellow]Add ConnectOnion to existing project?[/yellow]"):
148
+ console.print("[yellow]Initialization cancelled.[/yellow]")
149
+ return
150
+
151
+ # Auto-detect API keys from environment (no menu, just detect)
152
+ detected_keys = {}
153
+ provider = None
154
+
155
+ # Check for API keys in environment
156
+ env_api = check_environment_for_api_keys()
157
+ if env_api:
158
+ provider, env_key = env_api
159
+ detected_keys[provider] = env_key
160
+ if not yes:
161
+ console.print(f"[green]✓ Detected {provider.title()} API key[/green]")
162
+
163
+ # If --key provided via flag, use it
164
+ if key:
165
+ provider, key_type = detect_api_provider(key)
166
+ detected_keys[provider] = key
167
+
168
+ # Template selection - default to 'none' (just add .co folder) unless --template provided
169
+ if not template:
170
+ # No --template flag provided, just add ConnectOnion config
171
+ template = 'none'
172
+ if not yes:
173
+ console.print("\n[green]✓ Adding ConnectOnion config (.co folder)[/green] (use --template <name> for full templates)")
174
+ # else: template has a specific value from --template <name>
175
+
176
+ # Handle custom template
177
+ custom_code = None
178
+ if template == 'custom':
179
+ if not description and not yes:
180
+ console.print("\n[cyan]🤖 Describe your agent:[/cyan]")
181
+ description = Prompt.ask(" What should your agent do?")
182
+ elif not description:
183
+ description = "A general purpose agent"
184
+
185
+ show_progress("Generating custom template with AI...", 2.0)
186
+ # Use detected key or empty string (will use OPENONION_API_KEY after auth)
187
+ template_key = list(detected_keys.values())[0] if detected_keys else ""
188
+ custom_code = generate_custom_template(description, template_key)
189
+
190
+ # Start initialization
191
+ show_progress("Initializing ConnectOnion project...", 1.0)
192
+
193
+ # Get template directory
194
+ cli_dir = Path(__file__).parent.parent
195
+ template_dir = cli_dir / "templates" / template if template != 'none' else None
196
+
197
+ if template_dir and not template_dir.exists() and template not in ['custom', 'none']:
198
+ console.print(f"[red]❌ Template '{template}' not found![/red]")
199
+ return
200
+
201
+ # Copy template files
202
+ files_created = []
203
+ files_skipped = []
204
+
205
+ if template not in ['custom', 'none'] and template_dir and template_dir.exists():
206
+ for item in template_dir.iterdir():
207
+ # Skip hidden files except .env.example
208
+ if item.name.startswith('.') and item.name != '.env.example':
209
+ continue
210
+
211
+ dest_path = Path(current_dir) / item.name
212
+
213
+ if item.is_dir():
214
+ # Copy directory
215
+ if dest_path.exists() and not force:
216
+ files_skipped.append(f"{item.name}/ (already exists)")
217
+ else:
218
+ if dest_path.exists():
219
+ shutil.rmtree(dest_path)
220
+ shutil.copytree(item, dest_path)
221
+ files_created.append(f"{item.name}/")
222
+ else:
223
+ # Skip .env.example, we'll create .env directly
224
+ if item.name == '.env.example':
225
+ continue
226
+ # Copy file
227
+ if dest_path.exists() and not force:
228
+ files_skipped.append(f"{item.name} (already exists)")
229
+ else:
230
+ shutil.copy2(item, dest_path)
231
+ files_created.append(item.name)
232
+
233
+ # Create custom agent.py if custom template
234
+ if custom_code:
235
+ agent_file = Path(current_dir) / "agent.py"
236
+ agent_file.write_text(custom_code, encoding='utf-8')
237
+ files_created.append("agent.py")
238
+
239
+ # AUTHENTICATE FIRST - so we have OPENONION_API_KEY to add to .env
240
+ global_co_dir = Path.home() / ".co"
241
+ if not global_co_dir.exists():
242
+ ensure_global_config()
243
+
244
+ # Authenticate to get OPENONION_API_KEY (always, for everyone)
245
+ auth_success = authenticate(global_co_dir, save_to_project=False)
246
+
247
+ # Handle .env file - append API keys from global config
248
+ env_path = Path(current_dir) / ".env"
249
+ global_dir = Path.home() / ".co"
250
+ global_keys_env = global_dir / "keys.env"
251
+
252
+ # Read existing .env if it exists
253
+ existing_env_content = ""
254
+ existing_keys = set()
255
+ if env_path.exists():
256
+ with open(env_path, 'r', encoding='utf-8') as f:
257
+ existing_env_content = f.read()
258
+ # Parse existing keys
259
+ for line in existing_env_content.split('\n'):
260
+ if '=' in line and not line.strip().startswith('#'):
261
+ key = line.split('=')[0].strip()
262
+ existing_keys.add(key)
263
+
264
+ # Read global keys (now includes OPENONION_API_KEY from auth)
265
+ keys_to_add = []
266
+ if global_keys_env.exists():
267
+ with open(global_keys_env, 'r', encoding='utf-8') as f:
268
+ for line in f:
269
+ line = line.strip()
270
+ if line and not line.startswith('#') and '=' in line:
271
+ key = line.split('=')[0].strip()
272
+ if key not in existing_keys:
273
+ keys_to_add.append(line)
274
+
275
+ # Add agent address (from global keys.env)
276
+ if global_keys_env.exists():
277
+ # Load from global keys.env to get address
278
+ with open(global_keys_env, 'r', encoding='utf-8') as f:
279
+ for line in f:
280
+ line = line.strip()
281
+ if line.startswith('AGENT_ADDRESS=') and 'AGENT_ADDRESS' not in existing_keys:
282
+ keys_to_add.append(line)
283
+
284
+ # Add detected API keys
285
+ provider_to_env = {
286
+ "openai": "OPENAI_API_KEY",
287
+ "anthropic": "ANTHROPIC_API_KEY",
288
+ "google": "GEMINI_API_KEY",
289
+ "groq": "GROQ_API_KEY",
290
+ }
291
+ for prov, key_value in detected_keys.items():
292
+ env_var = provider_to_env.get(prov, f"{prov.upper()}_API_KEY")
293
+ if env_var not in existing_keys:
294
+ keys_to_add.append(f"{env_var}={key_value}")
295
+
296
+ # Write or append to .env
297
+ if not env_path.exists():
298
+ # Create new .env
299
+ if keys_to_add:
300
+ # Add global config path and default model comment
301
+ env_content = f"AGENT_CONFIG_PATH={Path.home() / '.co'}\n"
302
+ env_content += "# Default model: co/gemini-2.5-pro (managed keys with free credits)\n\n"
303
+ env_content += '\n'.join(keys_to_add) + '\n'
304
+ env_path.write_text(env_content, encoding='utf-8')
305
+ console.print(f"[green]✓ Saved to {env_path}[/green]")
306
+ else:
307
+ # Fallback - should not happen now that we always auth
308
+ env_content = """# Add your LLM API key(s) below (uncomment one and set value)
309
+ # OPENAI_API_KEY=
310
+ # ANTHROPIC_API_KEY=
311
+ # GEMINI_API_KEY=
312
+ # GROQ_API_KEY=
313
+
314
+ # Optional: Override default model
315
+ # MODEL=gpt-4o-mini
316
+ """
317
+ env_path.write_text(env_content, encoding='utf-8')
318
+ files_created.append(".env")
319
+ elif keys_to_add:
320
+ # Append missing keys to existing .env
321
+ with open(env_path, 'a', encoding='utf-8') as f:
322
+ if not existing_env_content.endswith('\n'):
323
+ f.write('\n')
324
+ f.write('\n# API Keys\n')
325
+ f.write('\n'.join(keys_to_add) + '\n')
326
+ console.print(f"[green]✓ Updated {env_path}[/green]")
327
+ files_created.append(".env (updated)")
328
+ else:
329
+ console.print("[green]✓ .env already contains all necessary keys[/green]")
330
+
331
+ # Create .co directory with metadata
332
+ co_dir = Path(current_dir) / ".co"
333
+ co_dir.mkdir(exist_ok=True)
334
+
335
+ # Create docs directory and copy documentation (always overwrite for latest version)
336
+ docs_dir = co_dir / "docs"
337
+ if docs_dir.exists():
338
+ shutil.rmtree(docs_dir)
339
+ docs_dir.mkdir(exist_ok=True)
340
+
341
+ # Copy ConnectOnion documentation
342
+ cli_dir = Path(__file__).parent.parent
343
+
344
+ # Always copy the vibe coding doc for all templates - it's the master reference doc
345
+ master_doc = cli_dir / "docs" / "co-vibecoding-principles-docs-contexts-all-in-one.md"
346
+
347
+ if master_doc.exists():
348
+ # Copy to .co/docs/ (project metadata)
349
+ target_doc = docs_dir / "co-vibecoding-principles-docs-contexts-all-in-one.md"
350
+ shutil.copy2(master_doc, target_doc)
351
+ files_created.append(".co/docs/co-vibecoding-principles-docs-contexts-all-in-one.md")
352
+
353
+ # ALSO copy to project root (always visible, easier to find)
354
+ root_doc = Path(current_dir) / "co-vibecoding-principles-docs-contexts-all-in-one.md"
355
+ shutil.copy2(master_doc, root_doc)
356
+ files_created.append("co-vibecoding-principles-docs-contexts-all-in-one.md")
357
+ else:
358
+ console.print(f"[yellow]⚠️ Warning: Vibe coding documentation not found at {master_doc}[/yellow]")
359
+
360
+ # Use global identity instead of generating project keys
361
+ # NO PROJECT KEYS - we use global address/email
362
+ # Reload global config to get updated email_active after authentication
363
+ global_config = toml.load(global_co_dir / "config.toml") if (global_co_dir / "config.toml").exists() else global_config
364
+ addr_data = global_config.get("agent", global_identity) # Use updated global identity
365
+
366
+ # Note: We're NOT creating project-specific keys anymore
367
+ # If user wants project-specific keys, they'll use 'co address' command
368
+
369
+ # Create config.toml (simplified - address/email now in .env)
370
+ config = {
371
+ "project": {
372
+ "name": os.path.basename(current_dir) or "connectonion-agent",
373
+ "created": datetime.now().isoformat(),
374
+ "framework_version": __version__,
375
+ "secrets": ".env", # Path to secrets file
376
+ },
377
+ "cli": {
378
+ "version": "1.0.0",
379
+ "command": "co init",
380
+ "template": template,
381
+ },
382
+ "agent": {
383
+ "algorithm": "ed25519",
384
+ "default_model": "co/gemini-2.5-pro", # Use managed model by default
385
+ "max_iterations": 10,
386
+ "created_at": datetime.now().isoformat(),
387
+ },
388
+ }
389
+
390
+ config_path = co_dir / "config.toml"
391
+ with open(config_path, "w", encoding='utf-8') as f:
392
+ toml.dump(config, f)
393
+ files_created.append(".co/config.toml")
394
+
395
+ # Handle .gitignore if in git repo
396
+ if (Path(current_dir) / ".git").exists():
397
+ gitignore_path = Path(current_dir) / ".gitignore"
398
+ gitignore_content = """
399
+ # ConnectOnion
400
+ .env
401
+ .co/keys/
402
+ .co/cache/
403
+ .co/logs/
404
+ .co/history/
405
+ co-vibecoding-principles-docs-contexts-all-in-one.md
406
+ *.py[cod]
407
+ __pycache__/
408
+ todo.md
409
+ """
410
+ if gitignore_path.exists():
411
+ with open(gitignore_path, "a", encoding='utf-8') as f:
412
+ if "# ConnectOnion" not in gitignore_path.read_text(encoding='utf-8'):
413
+ f.write(gitignore_content)
414
+ files_created.append(".gitignore (updated)")
415
+ else:
416
+ gitignore_path.write_text(gitignore_content.lstrip(), encoding='utf-8')
417
+ files_created.append(".gitignore")
418
+
419
+ # Success message with Rich formatting
420
+ console.print()
421
+ console.print(f"[bold green]✅ Initialized ConnectOnion in {project_name}[/bold green]")
422
+ console.print()
423
+
424
+ # Show different message based on whether agent.py exists
425
+ if template != 'none' and (Path(current_dir) / "agent.py").exists():
426
+ # Command with syntax highlighting - compact design
427
+ command = "python agent.py"
428
+ syntax = Syntax(
429
+ command,
430
+ "bash",
431
+ theme="monokai",
432
+ background_color="#272822", # Monokai background color
433
+ padding=(0, 1) # Minimal padding for tight fit
434
+ )
435
+ console.print(syntax)
436
+ console.print()
437
+
438
+ # Vibe Coding hint - clean formatting with proper spacing
439
+ console.print("[bold yellow]💡 Vibe Coding:[/bold yellow] Use Claude/Cursor/Codex with")
440
+ console.print(f" [cyan]co-vibecoding-principles-docs-contexts-all-in-one.md[/cyan]")
441
+ else:
442
+ # Vibe Coding hint for building from scratch
443
+ console.print("[bold yellow]💡 Vibe Coding:[/bold yellow] Use Claude/Cursor/Codex with")
444
+ console.print(f" [cyan]co-vibecoding-principles-docs-contexts-all-in-one.md[/cyan]")
445
+ console.print(" to build your agent")
446
+
447
+ # Resources - clean format with arrows for better alignment
448
+ console.print()
449
+ console.print("[bold cyan]📚 Resources:[/bold cyan]")
450
+ console.print(f" Docs [dim]→[/dim] [link=https://docs.connectonion.com][blue]https://docs.connectonion.com[/blue][/link]")
451
+ console.print(f" Discord [dim]→[/dim] [link=https://discord.gg/4xfD9k8AUF][blue]https://discord.gg/4xfD9k8AUF[/blue][/link]")
452
+ console.print(f" GitHub [dim]→[/dim] [link=https://github.com/openonion/connectonion][blue]https://github.com/openonion/connectonion[/blue][/link] [dim](⭐ star us!)[/dim]")
453
+ console.print()
454
+
455
+ # Clean up temporary project directory if created for authentication
456
+ if temp_project_dir and temp_project_dir.exists():
457
+ # Copy the auth token to the current project
458
+ temp_config = temp_project_dir / ".co" / "config.toml"
459
+ current_config = Path(current_dir) / ".co" / "config.toml"
460
+ if temp_config.exists() and current_config.exists():
461
+ temp_data = toml.load(temp_config)
462
+ current_data = toml.load(current_config)
463
+ if "auth" in temp_data:
464
+ current_data["auth"] = temp_data["auth"]
465
+ with open(current_config, "w", encoding='utf-8') as f:
466
+ toml.dump(current_data, f)
467
+
468
+ # Remove the temp directory
469
+ shutil.rmtree(temp_project_dir)