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,723 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AI Enhancement for How-To Guides (C3.3)
|
|
3
|
+
|
|
4
|
+
This module provides comprehensive AI enhancement for how-to guides with dual-mode support:
|
|
5
|
+
- API mode: Uses Claude API (requires ANTHROPIC_API_KEY)
|
|
6
|
+
- LOCAL mode: Uses Claude Code CLI (no API key needed)
|
|
7
|
+
|
|
8
|
+
Provides 5 automatic enhancements:
|
|
9
|
+
1. Step Descriptions - Natural language explanations (not just syntax)
|
|
10
|
+
2. Troubleshooting Solutions - Diagnostic flows + solutions for common errors
|
|
11
|
+
3. Prerequisites Explanations - Why each prerequisite is needed + setup instructions
|
|
12
|
+
4. Next Steps Suggestions - Related guides, variations, learning paths
|
|
13
|
+
5. Use Case Examples - Real-world scenarios showing when to use guide
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import json
|
|
17
|
+
import logging
|
|
18
|
+
import os
|
|
19
|
+
import subprocess
|
|
20
|
+
import tempfile
|
|
21
|
+
from dataclasses import dataclass, field
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
from typing import TYPE_CHECKING
|
|
24
|
+
|
|
25
|
+
# Avoid circular imports by using TYPE_CHECKING
|
|
26
|
+
if TYPE_CHECKING:
|
|
27
|
+
from .how_to_guide_builder import PrerequisiteItem, TroubleshootingItem
|
|
28
|
+
else:
|
|
29
|
+
# Import at runtime to avoid circular dependency issues
|
|
30
|
+
try:
|
|
31
|
+
from .how_to_guide_builder import PrerequisiteItem, TroubleshootingItem
|
|
32
|
+
except ImportError:
|
|
33
|
+
# Fallback definitions if import fails
|
|
34
|
+
@dataclass
|
|
35
|
+
class PrerequisiteItem:
|
|
36
|
+
name: str
|
|
37
|
+
why: str
|
|
38
|
+
setup: str
|
|
39
|
+
|
|
40
|
+
@dataclass
|
|
41
|
+
class TroubleshootingItem:
|
|
42
|
+
problem: str
|
|
43
|
+
symptoms: list[str] = field(default_factory=list)
|
|
44
|
+
solution: str = ""
|
|
45
|
+
diagnostic_steps: list[str] = field(default_factory=list)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
logger = logging.getLogger(__name__)
|
|
49
|
+
|
|
50
|
+
# Conditional import for Anthropic API
|
|
51
|
+
try:
|
|
52
|
+
import anthropic
|
|
53
|
+
|
|
54
|
+
ANTHROPIC_AVAILABLE = True
|
|
55
|
+
except ImportError:
|
|
56
|
+
ANTHROPIC_AVAILABLE = False
|
|
57
|
+
logger.debug("Anthropic library not available - API mode will be unavailable")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dataclass
|
|
61
|
+
class StepEnhancement:
|
|
62
|
+
"""Enhanced step information (internal use only)"""
|
|
63
|
+
|
|
64
|
+
step_index: int
|
|
65
|
+
explanation: str # Natural language explanation
|
|
66
|
+
variations: list[str] = field(default_factory=list) # Alternative approaches
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class GuideEnhancer:
|
|
70
|
+
"""
|
|
71
|
+
AI enhancement for how-to guides with dual-mode support.
|
|
72
|
+
|
|
73
|
+
Modes:
|
|
74
|
+
- api: Uses Claude API (requires ANTHROPIC_API_KEY)
|
|
75
|
+
- local: Uses Claude Code CLI (no API key needed)
|
|
76
|
+
- auto: Automatically detect best mode
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
def __init__(self, mode: str = "auto"):
|
|
80
|
+
"""
|
|
81
|
+
Initialize GuideEnhancer.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
mode: Enhancement mode - "api", "local", or "auto"
|
|
85
|
+
"""
|
|
86
|
+
self.mode = self._detect_mode(mode)
|
|
87
|
+
self.api_key = os.environ.get("ANTHROPIC_API_KEY")
|
|
88
|
+
self.client = None
|
|
89
|
+
|
|
90
|
+
if self.mode == "api":
|
|
91
|
+
if ANTHROPIC_AVAILABLE and self.api_key:
|
|
92
|
+
self.client = anthropic.Anthropic(api_key=self.api_key)
|
|
93
|
+
logger.info("✨ GuideEnhancer initialized in API mode")
|
|
94
|
+
else:
|
|
95
|
+
logger.warning(
|
|
96
|
+
"⚠️ API mode requested but anthropic library not available or no API key"
|
|
97
|
+
)
|
|
98
|
+
self.mode = "none"
|
|
99
|
+
elif self.mode == "local":
|
|
100
|
+
# Check if claude CLI is available
|
|
101
|
+
if not self._check_claude_cli():
|
|
102
|
+
logger.warning("⚠️ Claude CLI not found - falling back to API mode")
|
|
103
|
+
self.mode = "api"
|
|
104
|
+
if ANTHROPIC_AVAILABLE and self.api_key:
|
|
105
|
+
self.client = anthropic.Anthropic(api_key=self.api_key)
|
|
106
|
+
else:
|
|
107
|
+
logger.warning("⚠️ API fallback also unavailable")
|
|
108
|
+
self.mode = "none"
|
|
109
|
+
else:
|
|
110
|
+
logger.info("✨ GuideEnhancer initialized in LOCAL mode")
|
|
111
|
+
else:
|
|
112
|
+
logger.warning("⚠️ No AI enhancement available (no API key or Claude CLI)")
|
|
113
|
+
self.mode = "none"
|
|
114
|
+
|
|
115
|
+
def _detect_mode(self, requested_mode: str) -> str:
|
|
116
|
+
"""
|
|
117
|
+
Detect the best enhancement mode.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
requested_mode: User-requested mode
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
Detected mode: "api", "local", or "none"
|
|
124
|
+
"""
|
|
125
|
+
if requested_mode == "auto":
|
|
126
|
+
# Prefer API if key available, else LOCAL
|
|
127
|
+
if os.environ.get("ANTHROPIC_API_KEY") and ANTHROPIC_AVAILABLE:
|
|
128
|
+
return "api"
|
|
129
|
+
elif self._check_claude_cli():
|
|
130
|
+
return "local"
|
|
131
|
+
else:
|
|
132
|
+
return "none"
|
|
133
|
+
return requested_mode
|
|
134
|
+
|
|
135
|
+
def _check_claude_cli(self) -> bool:
|
|
136
|
+
"""Check if Claude Code CLI is available."""
|
|
137
|
+
try:
|
|
138
|
+
result = subprocess.run(
|
|
139
|
+
["claude", "--version"], capture_output=True, text=True, timeout=5
|
|
140
|
+
)
|
|
141
|
+
return result.returncode == 0
|
|
142
|
+
except (FileNotFoundError, subprocess.TimeoutExpired):
|
|
143
|
+
return False
|
|
144
|
+
|
|
145
|
+
def enhance_guide(self, guide_data: dict) -> dict:
|
|
146
|
+
"""
|
|
147
|
+
Apply all 5 enhancements to a guide.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
guide_data: Guide data dictionary with title, steps, etc.
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
Enhanced guide data with all 5 enhancements
|
|
154
|
+
"""
|
|
155
|
+
if self.mode == "none":
|
|
156
|
+
logger.warning("⚠️ AI enhancement unavailable - returning original guide")
|
|
157
|
+
return guide_data
|
|
158
|
+
|
|
159
|
+
try:
|
|
160
|
+
if self.mode == "api":
|
|
161
|
+
return self._enhance_via_api(guide_data)
|
|
162
|
+
else:
|
|
163
|
+
return self._enhance_via_local(guide_data)
|
|
164
|
+
except Exception as e:
|
|
165
|
+
logger.error(f"❌ AI enhancement failed: {e}")
|
|
166
|
+
logger.info("📝 Returning original guide without enhancement")
|
|
167
|
+
return guide_data
|
|
168
|
+
|
|
169
|
+
def enhance_step_descriptions(self, steps: list[dict]) -> list[StepEnhancement]:
|
|
170
|
+
"""
|
|
171
|
+
Enhancement 1: Add natural language explanations to steps.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
steps: List of workflow steps
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
List of step enhancements with explanations
|
|
178
|
+
"""
|
|
179
|
+
if not steps or self.mode == "none":
|
|
180
|
+
return []
|
|
181
|
+
|
|
182
|
+
prompt = self._create_step_description_prompt(steps)
|
|
183
|
+
response = self._call_ai(prompt)
|
|
184
|
+
|
|
185
|
+
if not response:
|
|
186
|
+
return []
|
|
187
|
+
|
|
188
|
+
try:
|
|
189
|
+
data = json.loads(response)
|
|
190
|
+
return [
|
|
191
|
+
StepEnhancement(
|
|
192
|
+
step_index=item.get("step_index", i),
|
|
193
|
+
explanation=item.get("explanation", ""),
|
|
194
|
+
variations=item.get("variations", []),
|
|
195
|
+
)
|
|
196
|
+
for i, item in enumerate(data.get("step_descriptions", []))
|
|
197
|
+
]
|
|
198
|
+
except (json.JSONDecodeError, KeyError) as e:
|
|
199
|
+
logger.warning(f"⚠️ Failed to parse step descriptions: {e}")
|
|
200
|
+
return []
|
|
201
|
+
|
|
202
|
+
def enhance_troubleshooting(self, guide_data: dict) -> list[TroubleshootingItem]:
|
|
203
|
+
"""
|
|
204
|
+
Enhancement 2: Generate diagnostic flows + solutions.
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
guide_data: Guide data with title, steps, language
|
|
208
|
+
|
|
209
|
+
Returns:
|
|
210
|
+
List of troubleshooting items with solutions
|
|
211
|
+
"""
|
|
212
|
+
if self.mode == "none":
|
|
213
|
+
return []
|
|
214
|
+
|
|
215
|
+
prompt = self._create_troubleshooting_prompt(guide_data)
|
|
216
|
+
response = self._call_ai(prompt)
|
|
217
|
+
|
|
218
|
+
if not response:
|
|
219
|
+
return []
|
|
220
|
+
|
|
221
|
+
try:
|
|
222
|
+
data = json.loads(response)
|
|
223
|
+
return [
|
|
224
|
+
TroubleshootingItem(
|
|
225
|
+
problem=item.get("problem", ""),
|
|
226
|
+
symptoms=item.get("symptoms", []),
|
|
227
|
+
diagnostic_steps=item.get("diagnostic_steps", []),
|
|
228
|
+
solution=item.get("solution", ""),
|
|
229
|
+
)
|
|
230
|
+
for item in data.get("troubleshooting", [])
|
|
231
|
+
]
|
|
232
|
+
except (json.JSONDecodeError, KeyError) as e:
|
|
233
|
+
logger.warning(f"⚠️ Failed to parse troubleshooting items: {e}")
|
|
234
|
+
return []
|
|
235
|
+
|
|
236
|
+
def enhance_prerequisites(self, prereqs: list[str]) -> list[PrerequisiteItem]:
|
|
237
|
+
"""
|
|
238
|
+
Enhancement 3: Explain why prerequisites are needed.
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
prereqs: List of prerequisite names
|
|
242
|
+
|
|
243
|
+
Returns:
|
|
244
|
+
List of enhanced prerequisites with explanations
|
|
245
|
+
"""
|
|
246
|
+
if not prereqs or self.mode == "none":
|
|
247
|
+
return []
|
|
248
|
+
|
|
249
|
+
prompt = self._create_prerequisites_prompt(prereqs)
|
|
250
|
+
response = self._call_ai(prompt)
|
|
251
|
+
|
|
252
|
+
if not response:
|
|
253
|
+
return []
|
|
254
|
+
|
|
255
|
+
try:
|
|
256
|
+
data = json.loads(response)
|
|
257
|
+
return [
|
|
258
|
+
PrerequisiteItem(
|
|
259
|
+
name=item.get("name", ""), why=item.get("why", ""), setup=item.get("setup", "")
|
|
260
|
+
)
|
|
261
|
+
for item in data.get("prerequisites_detailed", [])
|
|
262
|
+
]
|
|
263
|
+
except (json.JSONDecodeError, KeyError) as e:
|
|
264
|
+
logger.warning(f"⚠️ Failed to parse prerequisites: {e}")
|
|
265
|
+
return []
|
|
266
|
+
|
|
267
|
+
def enhance_next_steps(self, guide_data: dict) -> list[str]:
|
|
268
|
+
"""
|
|
269
|
+
Enhancement 4: Suggest related guides and variations.
|
|
270
|
+
|
|
271
|
+
Args:
|
|
272
|
+
guide_data: Guide data with title, topic
|
|
273
|
+
|
|
274
|
+
Returns:
|
|
275
|
+
List of next step suggestions
|
|
276
|
+
"""
|
|
277
|
+
if self.mode == "none":
|
|
278
|
+
return []
|
|
279
|
+
|
|
280
|
+
prompt = self._create_next_steps_prompt(guide_data)
|
|
281
|
+
response = self._call_ai(prompt)
|
|
282
|
+
|
|
283
|
+
if not response:
|
|
284
|
+
return []
|
|
285
|
+
|
|
286
|
+
try:
|
|
287
|
+
data = json.loads(response)
|
|
288
|
+
return data.get("next_steps", [])
|
|
289
|
+
except (json.JSONDecodeError, KeyError) as e:
|
|
290
|
+
logger.warning(f"⚠️ Failed to parse next steps: {e}")
|
|
291
|
+
return []
|
|
292
|
+
|
|
293
|
+
def enhance_use_cases(self, guide_data: dict) -> list[str]:
|
|
294
|
+
"""
|
|
295
|
+
Enhancement 5: Generate real-world scenario examples.
|
|
296
|
+
|
|
297
|
+
Args:
|
|
298
|
+
guide_data: Guide data with title, description
|
|
299
|
+
|
|
300
|
+
Returns:
|
|
301
|
+
List of use case examples
|
|
302
|
+
"""
|
|
303
|
+
if self.mode == "none":
|
|
304
|
+
return []
|
|
305
|
+
|
|
306
|
+
prompt = self._create_use_cases_prompt(guide_data)
|
|
307
|
+
response = self._call_ai(prompt)
|
|
308
|
+
|
|
309
|
+
if not response:
|
|
310
|
+
return []
|
|
311
|
+
|
|
312
|
+
try:
|
|
313
|
+
data = json.loads(response)
|
|
314
|
+
return data.get("use_cases", [])
|
|
315
|
+
except (json.JSONDecodeError, KeyError) as e:
|
|
316
|
+
logger.warning(f"⚠️ Failed to parse use cases: {e}")
|
|
317
|
+
return []
|
|
318
|
+
|
|
319
|
+
# === AI Call Methods ===
|
|
320
|
+
|
|
321
|
+
def _call_ai(self, prompt: str, max_tokens: int = 4000) -> str | None:
|
|
322
|
+
"""
|
|
323
|
+
Call AI with the given prompt.
|
|
324
|
+
|
|
325
|
+
Args:
|
|
326
|
+
prompt: Prompt text
|
|
327
|
+
max_tokens: Maximum tokens in response
|
|
328
|
+
|
|
329
|
+
Returns:
|
|
330
|
+
AI response text or None if failed
|
|
331
|
+
"""
|
|
332
|
+
if self.mode == "api":
|
|
333
|
+
return self._call_claude_api(prompt, max_tokens)
|
|
334
|
+
elif self.mode == "local":
|
|
335
|
+
return self._call_claude_local(prompt)
|
|
336
|
+
return None
|
|
337
|
+
|
|
338
|
+
def _call_claude_api(self, prompt: str, max_tokens: int = 4000) -> str | None:
|
|
339
|
+
"""
|
|
340
|
+
Call Claude API.
|
|
341
|
+
|
|
342
|
+
Args:
|
|
343
|
+
prompt: Prompt text
|
|
344
|
+
max_tokens: Maximum tokens in response
|
|
345
|
+
|
|
346
|
+
Returns:
|
|
347
|
+
API response text or None if failed
|
|
348
|
+
"""
|
|
349
|
+
if not self.client:
|
|
350
|
+
return None
|
|
351
|
+
|
|
352
|
+
try:
|
|
353
|
+
response = self.client.messages.create(
|
|
354
|
+
model="claude-sonnet-4-20250514",
|
|
355
|
+
max_tokens=max_tokens,
|
|
356
|
+
messages=[{"role": "user", "content": prompt}],
|
|
357
|
+
)
|
|
358
|
+
return response.content[0].text
|
|
359
|
+
except Exception as e:
|
|
360
|
+
logger.warning(f"⚠️ Claude API call failed: {e}")
|
|
361
|
+
return None
|
|
362
|
+
|
|
363
|
+
def _call_claude_local(self, prompt: str) -> str | None:
|
|
364
|
+
"""
|
|
365
|
+
Call Claude Code CLI.
|
|
366
|
+
|
|
367
|
+
Args:
|
|
368
|
+
prompt: Prompt text
|
|
369
|
+
|
|
370
|
+
Returns:
|
|
371
|
+
CLI response text or None if failed
|
|
372
|
+
"""
|
|
373
|
+
try:
|
|
374
|
+
# Create temporary prompt file
|
|
375
|
+
with tempfile.NamedTemporaryFile(mode="w", suffix=".md", delete=False) as f:
|
|
376
|
+
f.write(prompt)
|
|
377
|
+
prompt_file = f.name
|
|
378
|
+
|
|
379
|
+
# Run claude CLI
|
|
380
|
+
result = subprocess.run(
|
|
381
|
+
["claude", prompt_file],
|
|
382
|
+
capture_output=True,
|
|
383
|
+
text=True,
|
|
384
|
+
timeout=300, # 5 min timeout
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
# Clean up prompt file
|
|
388
|
+
Path(prompt_file).unlink(missing_ok=True)
|
|
389
|
+
|
|
390
|
+
if result.returncode == 0:
|
|
391
|
+
return result.stdout
|
|
392
|
+
else:
|
|
393
|
+
logger.warning(f"⚠️ Claude CLI failed: {result.stderr}")
|
|
394
|
+
return None
|
|
395
|
+
|
|
396
|
+
except (subprocess.TimeoutExpired, Exception) as e:
|
|
397
|
+
logger.warning(f"⚠️ Claude CLI execution failed: {e}")
|
|
398
|
+
return None
|
|
399
|
+
|
|
400
|
+
# === Prompt Creation Methods ===
|
|
401
|
+
|
|
402
|
+
def _enhance_via_api(self, guide_data: dict) -> dict:
|
|
403
|
+
"""
|
|
404
|
+
Enhance guide via API mode.
|
|
405
|
+
|
|
406
|
+
Args:
|
|
407
|
+
guide_data: Guide data dictionary
|
|
408
|
+
|
|
409
|
+
Returns:
|
|
410
|
+
Enhanced guide data
|
|
411
|
+
"""
|
|
412
|
+
prompt = self._create_enhancement_prompt(guide_data)
|
|
413
|
+
response = self._call_claude_api(prompt)
|
|
414
|
+
|
|
415
|
+
if not response:
|
|
416
|
+
return guide_data
|
|
417
|
+
|
|
418
|
+
return self._parse_enhancement_response(response, guide_data)
|
|
419
|
+
|
|
420
|
+
def _enhance_via_local(self, guide_data: dict) -> dict:
|
|
421
|
+
"""
|
|
422
|
+
Enhance guide via LOCAL mode.
|
|
423
|
+
|
|
424
|
+
Args:
|
|
425
|
+
guide_data: Guide data dictionary
|
|
426
|
+
|
|
427
|
+
Returns:
|
|
428
|
+
Enhanced guide data
|
|
429
|
+
"""
|
|
430
|
+
prompt = self._create_enhancement_prompt(guide_data)
|
|
431
|
+
response = self._call_claude_local(prompt)
|
|
432
|
+
|
|
433
|
+
if not response:
|
|
434
|
+
return guide_data
|
|
435
|
+
|
|
436
|
+
return self._parse_enhancement_response(response, guide_data)
|
|
437
|
+
|
|
438
|
+
def _create_enhancement_prompt(self, guide_data: dict) -> str:
|
|
439
|
+
"""
|
|
440
|
+
Create comprehensive enhancement prompt for all 5 enhancements.
|
|
441
|
+
|
|
442
|
+
Args:
|
|
443
|
+
guide_data: Guide data dictionary
|
|
444
|
+
|
|
445
|
+
Returns:
|
|
446
|
+
Complete prompt text
|
|
447
|
+
"""
|
|
448
|
+
title = guide_data.get("title", "Unknown Guide")
|
|
449
|
+
steps = guide_data.get("steps", [])
|
|
450
|
+
language = guide_data.get("language", "python")
|
|
451
|
+
prerequisites = guide_data.get("prerequisites", [])
|
|
452
|
+
|
|
453
|
+
steps_text = self._format_steps_for_prompt(steps)
|
|
454
|
+
prereqs_text = ", ".join(prerequisites) if prerequisites else "None specified"
|
|
455
|
+
|
|
456
|
+
prompt = f"""I need you to enhance this how-to guide with 5 improvements:
|
|
457
|
+
|
|
458
|
+
CURRENT GUIDE:
|
|
459
|
+
Title: {title}
|
|
460
|
+
Steps: {len(steps)} steps
|
|
461
|
+
Code Language: {language}
|
|
462
|
+
Prerequisites: {prereqs_text}
|
|
463
|
+
|
|
464
|
+
STEP CODE:
|
|
465
|
+
{steps_text}
|
|
466
|
+
|
|
467
|
+
YOUR TASK - Provide JSON output with these 5 enhancements:
|
|
468
|
+
|
|
469
|
+
1. STEP_DESCRIPTIONS: For each step, write natural language explanation (not just syntax)
|
|
470
|
+
- Explain what the code does
|
|
471
|
+
- Explain why it's needed
|
|
472
|
+
- Provide context and best practices
|
|
473
|
+
|
|
474
|
+
2. TROUBLESHOOTING: Generate 3-5 common errors with diagnostic flows + solutions
|
|
475
|
+
- Identify likely errors for this type of workflow
|
|
476
|
+
- Provide symptoms to recognize the error
|
|
477
|
+
- Give diagnostic steps to confirm the issue
|
|
478
|
+
- Provide clear solution steps
|
|
479
|
+
|
|
480
|
+
3. PREREQUISITES: Explain WHY each prerequisite is needed + setup instructions
|
|
481
|
+
- For each prerequisite, explain its purpose
|
|
482
|
+
- Provide installation/setup commands
|
|
483
|
+
- Explain when it's used in the workflow
|
|
484
|
+
|
|
485
|
+
4. NEXT_STEPS: Suggest 3-5 related guides, variations, learning paths
|
|
486
|
+
- Related guides that build on this one
|
|
487
|
+
- Variations (e.g., async version, different approaches)
|
|
488
|
+
- Next logical learning steps
|
|
489
|
+
|
|
490
|
+
5. USE_CASES: Provide 2-3 real-world scenarios when to use this guide
|
|
491
|
+
- Specific situations where this workflow applies
|
|
492
|
+
- Problems it solves
|
|
493
|
+
- When NOT to use this approach
|
|
494
|
+
|
|
495
|
+
OUTPUT FORMAT (strict JSON):
|
|
496
|
+
{{
|
|
497
|
+
"step_descriptions": [
|
|
498
|
+
{{"step_index": 0, "explanation": "...", "variations": ["..."]}},
|
|
499
|
+
{{"step_index": 1, "explanation": "...", "variations": ["..."]}},
|
|
500
|
+
...
|
|
501
|
+
],
|
|
502
|
+
"troubleshooting": [
|
|
503
|
+
{{
|
|
504
|
+
"problem": "ImportError: No module named 'requests'",
|
|
505
|
+
"symptoms": ["Import fails", "Module not found error"],
|
|
506
|
+
"diagnostic_steps": ["Check pip list", "Verify virtual env"],
|
|
507
|
+
"solution": "Run: pip install requests"
|
|
508
|
+
}},
|
|
509
|
+
...
|
|
510
|
+
],
|
|
511
|
+
"prerequisites_detailed": [
|
|
512
|
+
{{"name": "requests", "why": "HTTP client for making web requests", "setup": "pip install requests"}},
|
|
513
|
+
...
|
|
514
|
+
],
|
|
515
|
+
"next_steps": [
|
|
516
|
+
"How to handle async workflows",
|
|
517
|
+
"How to add error handling",
|
|
518
|
+
...
|
|
519
|
+
],
|
|
520
|
+
"use_cases": [
|
|
521
|
+
"Use when you need to automate web scraping tasks",
|
|
522
|
+
"Ideal for building documentation archives",
|
|
523
|
+
...
|
|
524
|
+
]
|
|
525
|
+
}}
|
|
526
|
+
|
|
527
|
+
IMPORTANT: Return ONLY valid JSON, no markdown code blocks or extra text.
|
|
528
|
+
"""
|
|
529
|
+
return prompt
|
|
530
|
+
|
|
531
|
+
def _create_step_description_prompt(self, steps: list[dict]) -> str:
|
|
532
|
+
"""Create prompt for step descriptions only."""
|
|
533
|
+
steps_text = self._format_steps_for_prompt(steps)
|
|
534
|
+
return f"""Generate natural language explanations for these code steps:
|
|
535
|
+
|
|
536
|
+
{steps_text}
|
|
537
|
+
|
|
538
|
+
Return JSON:
|
|
539
|
+
{{
|
|
540
|
+
"step_descriptions": [
|
|
541
|
+
{{"step_index": 0, "explanation": "...", "variations": [""]}},
|
|
542
|
+
...
|
|
543
|
+
]
|
|
544
|
+
}}
|
|
545
|
+
|
|
546
|
+
IMPORTANT: Return ONLY valid JSON.
|
|
547
|
+
"""
|
|
548
|
+
|
|
549
|
+
def _create_troubleshooting_prompt(self, guide_data: dict) -> str:
|
|
550
|
+
"""Create prompt for troubleshooting items."""
|
|
551
|
+
title = guide_data.get("title", "Unknown")
|
|
552
|
+
language = guide_data.get("language", "python")
|
|
553
|
+
steps = guide_data.get("steps", [])
|
|
554
|
+
steps_text = self._format_steps_for_prompt(steps)
|
|
555
|
+
|
|
556
|
+
return f"""Generate troubleshooting guidance for this {language} workflow:
|
|
557
|
+
|
|
558
|
+
Title: {title}
|
|
559
|
+
Steps:
|
|
560
|
+
{steps_text}
|
|
561
|
+
|
|
562
|
+
Return JSON with 3-5 common errors:
|
|
563
|
+
{{
|
|
564
|
+
"troubleshooting": [
|
|
565
|
+
{{
|
|
566
|
+
"problem": "...",
|
|
567
|
+
"symptoms": ["...", "..."],
|
|
568
|
+
"diagnostic_steps": ["...", "..."],
|
|
569
|
+
"solution": "..."
|
|
570
|
+
}},
|
|
571
|
+
...
|
|
572
|
+
]
|
|
573
|
+
}}
|
|
574
|
+
|
|
575
|
+
IMPORTANT: Return ONLY valid JSON.
|
|
576
|
+
"""
|
|
577
|
+
|
|
578
|
+
def _create_prerequisites_prompt(self, prereqs: list[str]) -> str:
|
|
579
|
+
"""Create prompt for prerequisites enhancement."""
|
|
580
|
+
prereqs_text = ", ".join(prereqs)
|
|
581
|
+
return f"""Explain why these prerequisites are needed and how to install them:
|
|
582
|
+
|
|
583
|
+
Prerequisites: {prereqs_text}
|
|
584
|
+
|
|
585
|
+
Return JSON:
|
|
586
|
+
{{
|
|
587
|
+
"prerequisites_detailed": [
|
|
588
|
+
{{"name": "...", "why": "...", "setup": "..."}},
|
|
589
|
+
...
|
|
590
|
+
]
|
|
591
|
+
}}
|
|
592
|
+
|
|
593
|
+
IMPORTANT: Return ONLY valid JSON.
|
|
594
|
+
"""
|
|
595
|
+
|
|
596
|
+
def _create_next_steps_prompt(self, guide_data: dict) -> str:
|
|
597
|
+
"""Create prompt for next steps suggestions."""
|
|
598
|
+
title = guide_data.get("title", "Unknown")
|
|
599
|
+
return f"""Suggest 3-5 related guides and learning paths after completing: {title}
|
|
600
|
+
|
|
601
|
+
Return JSON:
|
|
602
|
+
{{
|
|
603
|
+
"next_steps": [
|
|
604
|
+
"How to ...",
|
|
605
|
+
"How to ...",
|
|
606
|
+
...
|
|
607
|
+
]
|
|
608
|
+
}}
|
|
609
|
+
|
|
610
|
+
IMPORTANT: Return ONLY valid JSON.
|
|
611
|
+
"""
|
|
612
|
+
|
|
613
|
+
def _create_use_cases_prompt(self, guide_data: dict) -> str:
|
|
614
|
+
"""Create prompt for use case examples."""
|
|
615
|
+
title = guide_data.get("title", "Unknown")
|
|
616
|
+
description = guide_data.get("description", "")
|
|
617
|
+
|
|
618
|
+
return f"""Generate 2-3 real-world use cases for this guide:
|
|
619
|
+
|
|
620
|
+
Title: {title}
|
|
621
|
+
Description: {description}
|
|
622
|
+
|
|
623
|
+
Return JSON:
|
|
624
|
+
{{
|
|
625
|
+
"use_cases": [
|
|
626
|
+
"Use when you need to ...",
|
|
627
|
+
"Ideal for ...",
|
|
628
|
+
...
|
|
629
|
+
]
|
|
630
|
+
}}
|
|
631
|
+
|
|
632
|
+
IMPORTANT: Return ONLY valid JSON.
|
|
633
|
+
"""
|
|
634
|
+
|
|
635
|
+
def _format_steps_for_prompt(self, steps: list[dict]) -> str:
|
|
636
|
+
"""Format steps for inclusion in prompts."""
|
|
637
|
+
if not steps:
|
|
638
|
+
return "No steps provided"
|
|
639
|
+
|
|
640
|
+
formatted = []
|
|
641
|
+
for i, step in enumerate(steps):
|
|
642
|
+
desc = step.get("description", "")
|
|
643
|
+
code = step.get("code", "")
|
|
644
|
+
if code:
|
|
645
|
+
formatted.append(f"Step {i + 1}: {desc}\n```\n{code}\n```")
|
|
646
|
+
else:
|
|
647
|
+
formatted.append(f"Step {i + 1}: {desc}")
|
|
648
|
+
|
|
649
|
+
return "\n\n".join(formatted)
|
|
650
|
+
|
|
651
|
+
def _parse_enhancement_response(self, response: str, guide_data: dict) -> dict:
|
|
652
|
+
"""
|
|
653
|
+
Parse AI enhancement response.
|
|
654
|
+
|
|
655
|
+
Args:
|
|
656
|
+
response: AI response text (should be JSON)
|
|
657
|
+
guide_data: Original guide data
|
|
658
|
+
|
|
659
|
+
Returns:
|
|
660
|
+
Enhanced guide data
|
|
661
|
+
"""
|
|
662
|
+
try:
|
|
663
|
+
# Try to extract JSON from response (in case there's extra text)
|
|
664
|
+
json_start = response.find("{")
|
|
665
|
+
json_end = response.rfind("}") + 1
|
|
666
|
+
if json_start >= 0 and json_end > json_start:
|
|
667
|
+
json_text = response[json_start:json_end]
|
|
668
|
+
data = json.loads(json_text)
|
|
669
|
+
else:
|
|
670
|
+
data = json.loads(response)
|
|
671
|
+
|
|
672
|
+
# Merge enhancements into guide_data
|
|
673
|
+
enhanced = guide_data.copy()
|
|
674
|
+
|
|
675
|
+
# Step descriptions
|
|
676
|
+
if "step_descriptions" in data:
|
|
677
|
+
enhanced["step_enhancements"] = [
|
|
678
|
+
StepEnhancement(
|
|
679
|
+
step_index=item.get("step_index", i),
|
|
680
|
+
explanation=item.get("explanation", ""),
|
|
681
|
+
variations=item.get("variations", []),
|
|
682
|
+
)
|
|
683
|
+
for i, item in enumerate(data["step_descriptions"])
|
|
684
|
+
]
|
|
685
|
+
|
|
686
|
+
# Troubleshooting
|
|
687
|
+
if "troubleshooting" in data:
|
|
688
|
+
enhanced["troubleshooting_detailed"] = [
|
|
689
|
+
TroubleshootingItem(
|
|
690
|
+
problem=item.get("problem", ""),
|
|
691
|
+
symptoms=item.get("symptoms", []),
|
|
692
|
+
diagnostic_steps=item.get("diagnostic_steps", []),
|
|
693
|
+
solution=item.get("solution", ""),
|
|
694
|
+
)
|
|
695
|
+
for item in data["troubleshooting"]
|
|
696
|
+
]
|
|
697
|
+
|
|
698
|
+
# Prerequisites
|
|
699
|
+
if "prerequisites_detailed" in data:
|
|
700
|
+
enhanced["prerequisites_detailed"] = [
|
|
701
|
+
PrerequisiteItem(
|
|
702
|
+
name=item.get("name", ""),
|
|
703
|
+
why=item.get("why", ""),
|
|
704
|
+
setup=item.get("setup", ""),
|
|
705
|
+
)
|
|
706
|
+
for item in data["prerequisites_detailed"]
|
|
707
|
+
]
|
|
708
|
+
|
|
709
|
+
# Next steps
|
|
710
|
+
if "next_steps" in data:
|
|
711
|
+
enhanced["next_steps_detailed"] = data["next_steps"]
|
|
712
|
+
|
|
713
|
+
# Use cases
|
|
714
|
+
if "use_cases" in data:
|
|
715
|
+
enhanced["use_cases"] = data["use_cases"]
|
|
716
|
+
|
|
717
|
+
logger.info("✅ Successfully enhanced guide with all 5 improvements")
|
|
718
|
+
return enhanced
|
|
719
|
+
|
|
720
|
+
except (json.JSONDecodeError, KeyError) as e:
|
|
721
|
+
logger.warning(f"⚠️ Failed to parse AI response: {e}")
|
|
722
|
+
logger.debug(f"Response was: {response[:500]}...")
|
|
723
|
+
return guide_data
|