skill-seekers 2.7.3__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.
- skill_seekers/__init__.py +22 -0
- skill_seekers/cli/__init__.py +39 -0
- skill_seekers/cli/adaptors/__init__.py +120 -0
- skill_seekers/cli/adaptors/base.py +221 -0
- skill_seekers/cli/adaptors/claude.py +485 -0
- skill_seekers/cli/adaptors/gemini.py +453 -0
- skill_seekers/cli/adaptors/markdown.py +269 -0
- skill_seekers/cli/adaptors/openai.py +503 -0
- skill_seekers/cli/ai_enhancer.py +310 -0
- skill_seekers/cli/api_reference_builder.py +373 -0
- skill_seekers/cli/architectural_pattern_detector.py +525 -0
- skill_seekers/cli/code_analyzer.py +1462 -0
- skill_seekers/cli/codebase_scraper.py +1225 -0
- skill_seekers/cli/config_command.py +563 -0
- skill_seekers/cli/config_enhancer.py +431 -0
- skill_seekers/cli/config_extractor.py +871 -0
- skill_seekers/cli/config_manager.py +452 -0
- skill_seekers/cli/config_validator.py +394 -0
- skill_seekers/cli/conflict_detector.py +528 -0
- skill_seekers/cli/constants.py +72 -0
- skill_seekers/cli/dependency_analyzer.py +757 -0
- skill_seekers/cli/doc_scraper.py +2332 -0
- skill_seekers/cli/enhance_skill.py +488 -0
- skill_seekers/cli/enhance_skill_local.py +1096 -0
- skill_seekers/cli/enhance_status.py +194 -0
- skill_seekers/cli/estimate_pages.py +433 -0
- skill_seekers/cli/generate_router.py +1209 -0
- skill_seekers/cli/github_fetcher.py +534 -0
- skill_seekers/cli/github_scraper.py +1466 -0
- skill_seekers/cli/guide_enhancer.py +723 -0
- skill_seekers/cli/how_to_guide_builder.py +1267 -0
- skill_seekers/cli/install_agent.py +461 -0
- skill_seekers/cli/install_skill.py +178 -0
- skill_seekers/cli/language_detector.py +614 -0
- skill_seekers/cli/llms_txt_detector.py +60 -0
- skill_seekers/cli/llms_txt_downloader.py +104 -0
- skill_seekers/cli/llms_txt_parser.py +150 -0
- skill_seekers/cli/main.py +558 -0
- skill_seekers/cli/markdown_cleaner.py +132 -0
- skill_seekers/cli/merge_sources.py +806 -0
- skill_seekers/cli/package_multi.py +77 -0
- skill_seekers/cli/package_skill.py +241 -0
- skill_seekers/cli/pattern_recognizer.py +1825 -0
- skill_seekers/cli/pdf_extractor_poc.py +1166 -0
- skill_seekers/cli/pdf_scraper.py +617 -0
- skill_seekers/cli/quality_checker.py +519 -0
- skill_seekers/cli/rate_limit_handler.py +438 -0
- skill_seekers/cli/resume_command.py +160 -0
- skill_seekers/cli/run_tests.py +230 -0
- skill_seekers/cli/setup_wizard.py +93 -0
- skill_seekers/cli/split_config.py +390 -0
- skill_seekers/cli/swift_patterns.py +560 -0
- skill_seekers/cli/test_example_extractor.py +1081 -0
- skill_seekers/cli/test_unified_simple.py +179 -0
- skill_seekers/cli/unified_codebase_analyzer.py +572 -0
- skill_seekers/cli/unified_scraper.py +932 -0
- skill_seekers/cli/unified_skill_builder.py +1605 -0
- skill_seekers/cli/upload_skill.py +162 -0
- skill_seekers/cli/utils.py +432 -0
- skill_seekers/mcp/__init__.py +33 -0
- skill_seekers/mcp/agent_detector.py +316 -0
- skill_seekers/mcp/git_repo.py +273 -0
- skill_seekers/mcp/server.py +231 -0
- skill_seekers/mcp/server_fastmcp.py +1249 -0
- skill_seekers/mcp/server_legacy.py +2302 -0
- skill_seekers/mcp/source_manager.py +285 -0
- skill_seekers/mcp/tools/__init__.py +115 -0
- skill_seekers/mcp/tools/config_tools.py +251 -0
- skill_seekers/mcp/tools/packaging_tools.py +826 -0
- skill_seekers/mcp/tools/scraping_tools.py +842 -0
- skill_seekers/mcp/tools/source_tools.py +828 -0
- skill_seekers/mcp/tools/splitting_tools.py +212 -0
- skill_seekers/py.typed +0 -0
- skill_seekers-2.7.3.dist-info/METADATA +2027 -0
- skill_seekers-2.7.3.dist-info/RECORD +79 -0
- skill_seekers-2.7.3.dist-info/WHEEL +5 -0
- skill_seekers-2.7.3.dist-info/entry_points.txt +19 -0
- skill_seekers-2.7.3.dist-info/licenses/LICENSE +21 -0
- skill_seekers-2.7.3.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,563 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Interactive Configuration Wizard for Skill Seekers
|
|
3
|
+
|
|
4
|
+
Provides user-friendly setup for GitHub tokens, API keys, and settings.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import webbrowser
|
|
8
|
+
|
|
9
|
+
from .config_manager import get_config_manager
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def show_welcome_message():
|
|
13
|
+
"""Show first-run welcome message."""
|
|
14
|
+
print("""
|
|
15
|
+
╔═══════════════════════════════════════════════════════════════╗
|
|
16
|
+
║ ║
|
|
17
|
+
║ Welcome to Skill Seekers! 🎯 ║
|
|
18
|
+
║ ║
|
|
19
|
+
║ Convert documentation into LLM skills for Claude, Gemini, ║
|
|
20
|
+
║ OpenAI ChatGPT, and more! ║
|
|
21
|
+
║ ║
|
|
22
|
+
╚═══════════════════════════════════════════════════════════════╝
|
|
23
|
+
|
|
24
|
+
Quick Start:
|
|
25
|
+
|
|
26
|
+
1️⃣ Set up GitHub token (optional, but recommended):
|
|
27
|
+
$ skill-seekers config --github
|
|
28
|
+
|
|
29
|
+
2️⃣ Scrape documentation:
|
|
30
|
+
$ skill-seekers scrape --config configs/react.json
|
|
31
|
+
|
|
32
|
+
3️⃣ View available presets:
|
|
33
|
+
$ skill-seekers estimate --all
|
|
34
|
+
|
|
35
|
+
For more help:
|
|
36
|
+
$ skill-seekers --help
|
|
37
|
+
$ skill-seekers config --help
|
|
38
|
+
|
|
39
|
+
Documentation: https://github.com/SkillSeekers/skill-seekers
|
|
40
|
+
|
|
41
|
+
""")
|
|
42
|
+
|
|
43
|
+
config = get_config_manager()
|
|
44
|
+
|
|
45
|
+
# Ask if user wants to run setup now
|
|
46
|
+
response = input("Would you like to run the configuration wizard now? [y/N]: ").strip().lower()
|
|
47
|
+
|
|
48
|
+
if response in ["y", "yes"]:
|
|
49
|
+
main_menu()
|
|
50
|
+
else:
|
|
51
|
+
print("\nYou can run the configuration wizard anytime with:")
|
|
52
|
+
print(" $ skill-seekers config\n")
|
|
53
|
+
|
|
54
|
+
config.mark_welcome_shown()
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def main_menu():
|
|
58
|
+
"""Show main configuration menu."""
|
|
59
|
+
config = get_config_manager()
|
|
60
|
+
|
|
61
|
+
while True:
|
|
62
|
+
print("\n╔═══════════════════════════════════════════════════╗")
|
|
63
|
+
print("║ Skill Seekers Configuration ║")
|
|
64
|
+
print("╚═══════════════════════════════════════════════════╝\n")
|
|
65
|
+
|
|
66
|
+
print(" 1. GitHub Token Setup")
|
|
67
|
+
print(" 2. API Keys (Claude, Gemini, OpenAI)")
|
|
68
|
+
print(" 3. Rate Limit Settings")
|
|
69
|
+
print(" 4. Resume Settings")
|
|
70
|
+
print(" 5. View Current Configuration")
|
|
71
|
+
print(" 6. Test Connections")
|
|
72
|
+
print(" 7. Clean Up Old Progress Files")
|
|
73
|
+
print(" 0. Exit\n")
|
|
74
|
+
|
|
75
|
+
choice = input("Select an option [0-7]: ").strip()
|
|
76
|
+
|
|
77
|
+
if choice == "1":
|
|
78
|
+
github_token_menu()
|
|
79
|
+
elif choice == "2":
|
|
80
|
+
api_keys_menu()
|
|
81
|
+
elif choice == "3":
|
|
82
|
+
rate_limit_settings()
|
|
83
|
+
elif choice == "4":
|
|
84
|
+
resume_settings()
|
|
85
|
+
elif choice == "5":
|
|
86
|
+
config.display_config_summary()
|
|
87
|
+
input("\nPress Enter to continue...")
|
|
88
|
+
elif choice == "6":
|
|
89
|
+
test_connections()
|
|
90
|
+
elif choice == "7":
|
|
91
|
+
config.cleanup_old_progress()
|
|
92
|
+
input("\nPress Enter to continue...")
|
|
93
|
+
elif choice == "0":
|
|
94
|
+
print("\n✅ Configuration saved. Happy scraping! 🚀\n")
|
|
95
|
+
break
|
|
96
|
+
else:
|
|
97
|
+
print("❌ Invalid choice. Please try again.")
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def github_token_menu():
|
|
101
|
+
"""GitHub token configuration menu."""
|
|
102
|
+
config = get_config_manager()
|
|
103
|
+
|
|
104
|
+
while True:
|
|
105
|
+
print("\n╔═══════════════════════════════════════════════════╗")
|
|
106
|
+
print("║ GitHub Token Management ║")
|
|
107
|
+
print("╚═══════════════════════════════════════════════════╝\n")
|
|
108
|
+
|
|
109
|
+
profiles = config.list_github_profiles()
|
|
110
|
+
|
|
111
|
+
if profiles:
|
|
112
|
+
print("Current Profiles:\n")
|
|
113
|
+
for p in profiles:
|
|
114
|
+
default = " ⭐ (default)" if p["is_default"] else ""
|
|
115
|
+
print(f" • {p['name']}{default}")
|
|
116
|
+
if p["description"]:
|
|
117
|
+
print(f" {p['description']}")
|
|
118
|
+
print(f" Strategy: {p['strategy']}, Timeout: {p['timeout']}m\n")
|
|
119
|
+
else:
|
|
120
|
+
print("No GitHub profiles configured.\n")
|
|
121
|
+
|
|
122
|
+
print("Options:")
|
|
123
|
+
print(" 1. Add New Profile")
|
|
124
|
+
print(" 2. Remove Profile")
|
|
125
|
+
print(" 3. Set Default Profile")
|
|
126
|
+
print(" 4. Open GitHub Token Page")
|
|
127
|
+
print(" 0. Back to Main Menu\n")
|
|
128
|
+
|
|
129
|
+
choice = input("Select an option [0-4]: ").strip()
|
|
130
|
+
|
|
131
|
+
if choice == "1":
|
|
132
|
+
add_github_profile()
|
|
133
|
+
elif choice == "2":
|
|
134
|
+
remove_github_profile()
|
|
135
|
+
elif choice == "3":
|
|
136
|
+
set_default_profile()
|
|
137
|
+
elif choice == "4":
|
|
138
|
+
open_github_token_page()
|
|
139
|
+
elif choice == "0":
|
|
140
|
+
break
|
|
141
|
+
else:
|
|
142
|
+
print("❌ Invalid choice. Please try again.")
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def add_github_profile():
|
|
146
|
+
"""Add a new GitHub profile interactively."""
|
|
147
|
+
config = get_config_manager()
|
|
148
|
+
|
|
149
|
+
print("\n📝 Add New GitHub Profile\n")
|
|
150
|
+
|
|
151
|
+
# Profile name
|
|
152
|
+
while True:
|
|
153
|
+
name = input("Profile name (e.g., 'personal', 'work'): ").strip()
|
|
154
|
+
if not name:
|
|
155
|
+
print("❌ Profile name cannot be empty.")
|
|
156
|
+
continue
|
|
157
|
+
if name in config.config["github"]["profiles"]:
|
|
158
|
+
print(f"❌ Profile '{name}' already exists.")
|
|
159
|
+
overwrite = input("Overwrite? [y/N]: ").strip().lower()
|
|
160
|
+
if overwrite not in ["y", "yes"]:
|
|
161
|
+
continue
|
|
162
|
+
break
|
|
163
|
+
|
|
164
|
+
# Description
|
|
165
|
+
description = input("Description (optional): ").strip()
|
|
166
|
+
|
|
167
|
+
# Token
|
|
168
|
+
print("\nTo create a GitHub token:")
|
|
169
|
+
print(" 1. Go to: https://github.com/settings/tokens")
|
|
170
|
+
print(" 2. Click 'Generate new token' → 'Generate new token (classic)'")
|
|
171
|
+
print(" 3. Scopes needed:")
|
|
172
|
+
print(" • For public repos: 'public_repo'")
|
|
173
|
+
print(" • For private repos: 'repo' (full access)")
|
|
174
|
+
print(" 4. Copy the token (ghp_...)\n")
|
|
175
|
+
|
|
176
|
+
open_now = input("Open GitHub token page in browser? [Y/n]: ").strip().lower()
|
|
177
|
+
if open_now not in ["n", "no"]:
|
|
178
|
+
open_github_token_page()
|
|
179
|
+
|
|
180
|
+
while True:
|
|
181
|
+
token = input("\nGitHub token (ghp_...): ").strip()
|
|
182
|
+
if not token:
|
|
183
|
+
print("❌ Token cannot be empty.")
|
|
184
|
+
continue
|
|
185
|
+
if not (token.startswith("ghp_") or token.startswith("github_pat_")):
|
|
186
|
+
print("⚠️ Warning: Token doesn't match GitHub format")
|
|
187
|
+
proceed = input("Continue anyway? [y/N]: ").strip().lower()
|
|
188
|
+
if proceed not in ["y", "yes"]:
|
|
189
|
+
continue
|
|
190
|
+
break
|
|
191
|
+
|
|
192
|
+
# Rate limit strategy
|
|
193
|
+
print("\nRate Limit Strategy:")
|
|
194
|
+
print(" 1. prompt - Ask what to do (default)")
|
|
195
|
+
print(" 2. wait - Wait until reset")
|
|
196
|
+
print(" 3. switch - Try another profile")
|
|
197
|
+
print(" 4. fail - Fail immediately")
|
|
198
|
+
|
|
199
|
+
strategy_choice = input("\nSelect strategy [1-4] (default: 1): ").strip() or "1"
|
|
200
|
+
strategy_map = {"1": "prompt", "2": "wait", "3": "switch", "4": "fail"}
|
|
201
|
+
strategy = strategy_map.get(strategy_choice, "prompt")
|
|
202
|
+
|
|
203
|
+
# Timeout
|
|
204
|
+
timeout_input = input("\nTimeout in minutes (default: 30): ").strip() or "30"
|
|
205
|
+
try:
|
|
206
|
+
timeout = int(timeout_input)
|
|
207
|
+
except ValueError:
|
|
208
|
+
print("⚠️ Invalid timeout, using default 30 minutes")
|
|
209
|
+
timeout = 30
|
|
210
|
+
|
|
211
|
+
# Set as default
|
|
212
|
+
has_profiles = bool(config.config["github"]["profiles"])
|
|
213
|
+
if has_profiles:
|
|
214
|
+
set_default = input("\nSet as default profile? [y/N]: ").strip().lower() in ["y", "yes"]
|
|
215
|
+
else:
|
|
216
|
+
set_default = True # First profile is always default
|
|
217
|
+
|
|
218
|
+
# Add profile
|
|
219
|
+
config.add_github_profile(
|
|
220
|
+
name=name,
|
|
221
|
+
token=token,
|
|
222
|
+
description=description,
|
|
223
|
+
rate_limit_strategy=strategy,
|
|
224
|
+
timeout_minutes=timeout,
|
|
225
|
+
set_as_default=set_default,
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
print(f"\n✅ GitHub profile '{name}' added successfully!")
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def remove_github_profile():
|
|
232
|
+
"""Remove a GitHub profile."""
|
|
233
|
+
config = get_config_manager()
|
|
234
|
+
|
|
235
|
+
profiles = config.list_github_profiles()
|
|
236
|
+
if not profiles:
|
|
237
|
+
print("\n❌ No profiles to remove.")
|
|
238
|
+
return
|
|
239
|
+
|
|
240
|
+
print("\n🗑️ Remove GitHub Profile\n")
|
|
241
|
+
print("Available profiles:")
|
|
242
|
+
for idx, p in enumerate(profiles, 1):
|
|
243
|
+
default = " (default)" if p["is_default"] else ""
|
|
244
|
+
print(f" {idx}. {p['name']}{default}")
|
|
245
|
+
|
|
246
|
+
choice = input(f"\nSelect profile to remove [1-{len(profiles)}] or 0 to cancel: ").strip()
|
|
247
|
+
|
|
248
|
+
try:
|
|
249
|
+
choice_idx = int(choice)
|
|
250
|
+
if choice_idx == 0:
|
|
251
|
+
return
|
|
252
|
+
if 1 <= choice_idx <= len(profiles):
|
|
253
|
+
profile_name = profiles[choice_idx - 1]["name"]
|
|
254
|
+
confirm = input(f"Really remove profile '{profile_name}'? [y/N]: ").strip().lower()
|
|
255
|
+
if confirm in ["y", "yes"]:
|
|
256
|
+
config.remove_github_profile(profile_name)
|
|
257
|
+
else:
|
|
258
|
+
print("❌ Invalid choice.")
|
|
259
|
+
except ValueError:
|
|
260
|
+
print("❌ Invalid input.")
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def set_default_profile():
|
|
264
|
+
"""Set default GitHub profile."""
|
|
265
|
+
config = get_config_manager()
|
|
266
|
+
|
|
267
|
+
profiles = config.list_github_profiles()
|
|
268
|
+
if not profiles:
|
|
269
|
+
print("\n❌ No profiles available.")
|
|
270
|
+
return
|
|
271
|
+
|
|
272
|
+
print("\n⭐ Set Default GitHub Profile\n")
|
|
273
|
+
print("Available profiles:")
|
|
274
|
+
for idx, p in enumerate(profiles, 1):
|
|
275
|
+
default = " (current default)" if p["is_default"] else ""
|
|
276
|
+
print(f" {idx}. {p['name']}{default}")
|
|
277
|
+
|
|
278
|
+
choice = input(f"\nSelect default profile [1-{len(profiles)}] or 0 to cancel: ").strip()
|
|
279
|
+
|
|
280
|
+
try:
|
|
281
|
+
choice_idx = int(choice)
|
|
282
|
+
if choice_idx == 0:
|
|
283
|
+
return
|
|
284
|
+
if 1 <= choice_idx <= len(profiles):
|
|
285
|
+
profile_name = profiles[choice_idx - 1]["name"]
|
|
286
|
+
config.config["github"]["default_profile"] = profile_name
|
|
287
|
+
config.save_config()
|
|
288
|
+
print(f"\n✅ Set '{profile_name}' as default profile")
|
|
289
|
+
else:
|
|
290
|
+
print("❌ Invalid choice.")
|
|
291
|
+
except ValueError:
|
|
292
|
+
print("❌ Invalid input.")
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def open_github_token_page():
|
|
296
|
+
"""Open GitHub token creation page in browser."""
|
|
297
|
+
url = "https://github.com/settings/tokens/new"
|
|
298
|
+
print(f"\n🌐 Opening {url}...")
|
|
299
|
+
try:
|
|
300
|
+
webbrowser.open(url)
|
|
301
|
+
print("✅ Opened in browser")
|
|
302
|
+
except Exception as e:
|
|
303
|
+
print(f"⚠️ Could not open browser: {e}")
|
|
304
|
+
print(f" Please visit: {url}")
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def api_keys_menu():
|
|
308
|
+
"""API keys configuration menu."""
|
|
309
|
+
config = get_config_manager()
|
|
310
|
+
|
|
311
|
+
print("\n╔═══════════════════════════════════════════════════╗")
|
|
312
|
+
print("║ API Keys Management ║")
|
|
313
|
+
print("╚═══════════════════════════════════════════════════╝\n")
|
|
314
|
+
|
|
315
|
+
print("Current status:")
|
|
316
|
+
for provider in ["anthropic", "google", "openai"]:
|
|
317
|
+
key = config.get_api_key(provider)
|
|
318
|
+
status = "✅ Set" if key else "❌ Not set"
|
|
319
|
+
source = ""
|
|
320
|
+
if key:
|
|
321
|
+
import os
|
|
322
|
+
|
|
323
|
+
env_var = {
|
|
324
|
+
"anthropic": "ANTHROPIC_API_KEY",
|
|
325
|
+
"google": "GOOGLE_API_KEY",
|
|
326
|
+
"openai": "OPENAI_API_KEY",
|
|
327
|
+
}[provider]
|
|
328
|
+
source = " (from environment)" if os.getenv(env_var) else " (from config)"
|
|
329
|
+
print(f" • {provider.capitalize()}: {status}{source}")
|
|
330
|
+
|
|
331
|
+
print("\nOptions:")
|
|
332
|
+
print(" 1. Set Anthropic (Claude) API Key")
|
|
333
|
+
print(" 2. Set Google (Gemini) API Key")
|
|
334
|
+
print(" 3. Set OpenAI (ChatGPT) API Key")
|
|
335
|
+
print(" 0. Back to Main Menu\n")
|
|
336
|
+
|
|
337
|
+
choice = input("Select an option [0-3]: ").strip()
|
|
338
|
+
|
|
339
|
+
provider_map = {
|
|
340
|
+
"1": ("anthropic", "https://console.anthropic.com/settings/keys"),
|
|
341
|
+
"2": ("google", "https://makersuite.google.com/app/apikey"),
|
|
342
|
+
"3": ("openai", "https://platform.openai.com/api-keys"),
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if choice in provider_map:
|
|
346
|
+
provider, url = provider_map[choice]
|
|
347
|
+
set_api_key(provider, url)
|
|
348
|
+
elif choice != "0":
|
|
349
|
+
print("❌ Invalid choice.")
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def set_api_key(provider: str, url: str):
|
|
353
|
+
"""Set an API key interactively."""
|
|
354
|
+
config = get_config_manager()
|
|
355
|
+
|
|
356
|
+
print(f"\n🔑 Set {provider.capitalize()} API Key\n")
|
|
357
|
+
print(f"Get your API key at: {url}\n")
|
|
358
|
+
|
|
359
|
+
open_now = input("Open in browser? [Y/n]: ").strip().lower()
|
|
360
|
+
if open_now not in ["n", "no"]:
|
|
361
|
+
try:
|
|
362
|
+
webbrowser.open(url)
|
|
363
|
+
print("✅ Opened in browser\n")
|
|
364
|
+
except Exception:
|
|
365
|
+
pass
|
|
366
|
+
|
|
367
|
+
key = input(f"Enter {provider.capitalize()} API key (or leave empty to skip): ").strip()
|
|
368
|
+
|
|
369
|
+
if key:
|
|
370
|
+
config.set_api_key(provider, key)
|
|
371
|
+
else:
|
|
372
|
+
print("⏭️ Skipped")
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
def rate_limit_settings():
|
|
376
|
+
"""Configure rate limit settings."""
|
|
377
|
+
config = get_config_manager()
|
|
378
|
+
|
|
379
|
+
print("\n╔═══════════════════════════════════════════════════╗")
|
|
380
|
+
print("║ Rate Limit Settings ║")
|
|
381
|
+
print("╚═══════════════════════════════════════════════════╝\n")
|
|
382
|
+
|
|
383
|
+
current = config.config["rate_limit"]
|
|
384
|
+
|
|
385
|
+
print("Current settings:")
|
|
386
|
+
print(f" • Default timeout: {current['default_timeout_minutes']} minutes")
|
|
387
|
+
print(f" • Auto-switch profiles: {current['auto_switch_profiles']}")
|
|
388
|
+
print(f" • Show countdown: {current['show_countdown']}\n")
|
|
389
|
+
|
|
390
|
+
# Timeout
|
|
391
|
+
timeout_input = input(
|
|
392
|
+
f"Default timeout in minutes [{current['default_timeout_minutes']}]: "
|
|
393
|
+
).strip()
|
|
394
|
+
if timeout_input:
|
|
395
|
+
try:
|
|
396
|
+
config.config["rate_limit"]["default_timeout_minutes"] = int(timeout_input)
|
|
397
|
+
except ValueError:
|
|
398
|
+
print("⚠️ Invalid input, keeping current value")
|
|
399
|
+
|
|
400
|
+
# Auto-switch
|
|
401
|
+
auto_switch_input = (
|
|
402
|
+
input(f"Auto-switch to other profiles? [y/n] ({current['auto_switch_profiles']}): ")
|
|
403
|
+
.strip()
|
|
404
|
+
.lower()
|
|
405
|
+
)
|
|
406
|
+
if auto_switch_input:
|
|
407
|
+
config.config["rate_limit"]["auto_switch_profiles"] = auto_switch_input in ["y", "yes"]
|
|
408
|
+
|
|
409
|
+
# Show countdown
|
|
410
|
+
countdown_input = (
|
|
411
|
+
input(f"Show countdown timer? [y/n] ({current['show_countdown']}): ").strip().lower()
|
|
412
|
+
)
|
|
413
|
+
if countdown_input:
|
|
414
|
+
config.config["rate_limit"]["show_countdown"] = countdown_input in ["y", "yes"]
|
|
415
|
+
|
|
416
|
+
config.save_config()
|
|
417
|
+
print("\n✅ Rate limit settings updated")
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
def resume_settings():
|
|
421
|
+
"""Configure resume/progress settings."""
|
|
422
|
+
config = get_config_manager()
|
|
423
|
+
|
|
424
|
+
print("\n╔═══════════════════════════════════════════════════╗")
|
|
425
|
+
print("║ Resume Settings ║")
|
|
426
|
+
print("╚═══════════════════════════════════════════════════╝\n")
|
|
427
|
+
|
|
428
|
+
current = config.config["resume"]
|
|
429
|
+
|
|
430
|
+
print("Current settings:")
|
|
431
|
+
print(f" • Auto-save interval: {current['auto_save_interval_seconds']} seconds")
|
|
432
|
+
print(f" • Keep progress for: {current['keep_progress_days']} days\n")
|
|
433
|
+
|
|
434
|
+
# Auto-save interval
|
|
435
|
+
interval_input = input(
|
|
436
|
+
f"Auto-save interval in seconds [{current['auto_save_interval_seconds']}]: "
|
|
437
|
+
).strip()
|
|
438
|
+
if interval_input:
|
|
439
|
+
try:
|
|
440
|
+
config.config["resume"]["auto_save_interval_seconds"] = int(interval_input)
|
|
441
|
+
except ValueError:
|
|
442
|
+
print("⚠️ Invalid input, keeping current value")
|
|
443
|
+
|
|
444
|
+
# Keep days
|
|
445
|
+
days_input = input(
|
|
446
|
+
f"Keep progress for how many days [{current['keep_progress_days']}]: "
|
|
447
|
+
).strip()
|
|
448
|
+
if days_input:
|
|
449
|
+
try:
|
|
450
|
+
config.config["resume"]["keep_progress_days"] = int(days_input)
|
|
451
|
+
except ValueError:
|
|
452
|
+
print("⚠️ Invalid input, keeping current value")
|
|
453
|
+
|
|
454
|
+
config.save_config()
|
|
455
|
+
print("\n✅ Resume settings updated")
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
def test_connections():
|
|
459
|
+
"""Test GitHub and API connections."""
|
|
460
|
+
config = get_config_manager()
|
|
461
|
+
|
|
462
|
+
print("\n╔═══════════════════════════════════════════════════╗")
|
|
463
|
+
print("║ Connection Tests ║")
|
|
464
|
+
print("╚═══════════════════════════════════════════════════╝\n")
|
|
465
|
+
|
|
466
|
+
# Test GitHub tokens
|
|
467
|
+
print("Testing GitHub tokens...")
|
|
468
|
+
profiles = config.list_github_profiles()
|
|
469
|
+
|
|
470
|
+
if not profiles:
|
|
471
|
+
print(" ⚠️ No GitHub profiles configured")
|
|
472
|
+
else:
|
|
473
|
+
import requests
|
|
474
|
+
|
|
475
|
+
for p in profiles:
|
|
476
|
+
token = config.config["github"]["profiles"][p["name"]]["token"]
|
|
477
|
+
try:
|
|
478
|
+
response = requests.get(
|
|
479
|
+
"https://api.github.com/rate_limit",
|
|
480
|
+
headers={"Authorization": f"token {token}"},
|
|
481
|
+
timeout=5,
|
|
482
|
+
)
|
|
483
|
+
if response.status_code == 200:
|
|
484
|
+
data = response.json()
|
|
485
|
+
remaining = data["rate"]["remaining"]
|
|
486
|
+
limit = data["rate"]["limit"]
|
|
487
|
+
print(f" ✅ {p['name']}: {remaining}/{limit} requests remaining")
|
|
488
|
+
else:
|
|
489
|
+
print(f" ❌ {p['name']}: Invalid token (status {response.status_code})")
|
|
490
|
+
except Exception as e:
|
|
491
|
+
print(f" ❌ {p['name']}: Connection failed - {e}")
|
|
492
|
+
|
|
493
|
+
print()
|
|
494
|
+
|
|
495
|
+
# Test API keys
|
|
496
|
+
print("Testing API keys...")
|
|
497
|
+
|
|
498
|
+
# Anthropic
|
|
499
|
+
anthropic_key = config.get_api_key("anthropic")
|
|
500
|
+
if anthropic_key:
|
|
501
|
+
print(" ℹ️ Anthropic: Key configured (test would consume credits)")
|
|
502
|
+
else:
|
|
503
|
+
print(" ⚠️ Anthropic: Not configured")
|
|
504
|
+
|
|
505
|
+
# Google
|
|
506
|
+
google_key = config.get_api_key("google")
|
|
507
|
+
if google_key:
|
|
508
|
+
print(" ℹ️ Google: Key configured (test would consume quota)")
|
|
509
|
+
else:
|
|
510
|
+
print(" ⚠️ Google: Not configured")
|
|
511
|
+
|
|
512
|
+
# OpenAI
|
|
513
|
+
openai_key = config.get_api_key("openai")
|
|
514
|
+
if openai_key:
|
|
515
|
+
print(" ℹ️ OpenAI: Key configured (test would consume credits)")
|
|
516
|
+
else:
|
|
517
|
+
print(" ⚠️ OpenAI: Not configured")
|
|
518
|
+
|
|
519
|
+
input("\nPress Enter to continue...")
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
def main():
|
|
523
|
+
"""Main entry point for config command."""
|
|
524
|
+
import argparse
|
|
525
|
+
|
|
526
|
+
parser = argparse.ArgumentParser(description="Configure Skill Seekers settings")
|
|
527
|
+
parser.add_argument("--github", action="store_true", help="Go directly to GitHub token setup")
|
|
528
|
+
parser.add_argument("--api-keys", action="store_true", help="Go directly to API keys setup")
|
|
529
|
+
parser.add_argument("--show", action="store_true", help="Show current configuration and exit")
|
|
530
|
+
parser.add_argument("--test", action="store_true", help="Test connections and exit")
|
|
531
|
+
parser.add_argument("--welcome", action="store_true", help="Show welcome message")
|
|
532
|
+
|
|
533
|
+
args = parser.parse_args()
|
|
534
|
+
|
|
535
|
+
config = get_config_manager()
|
|
536
|
+
|
|
537
|
+
# Handle direct options
|
|
538
|
+
if args.welcome:
|
|
539
|
+
show_welcome_message()
|
|
540
|
+
return
|
|
541
|
+
|
|
542
|
+
if args.show:
|
|
543
|
+
config.display_config_summary()
|
|
544
|
+
return
|
|
545
|
+
|
|
546
|
+
if args.test:
|
|
547
|
+
test_connections()
|
|
548
|
+
return
|
|
549
|
+
|
|
550
|
+
if args.github:
|
|
551
|
+
github_token_menu()
|
|
552
|
+
return
|
|
553
|
+
|
|
554
|
+
if args.api_keys:
|
|
555
|
+
api_keys_menu()
|
|
556
|
+
return
|
|
557
|
+
|
|
558
|
+
# Show main menu
|
|
559
|
+
main_menu()
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
if __name__ == "__main__":
|
|
563
|
+
main()
|