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.
Files changed (79) hide show
  1. skill_seekers/__init__.py +22 -0
  2. skill_seekers/cli/__init__.py +39 -0
  3. skill_seekers/cli/adaptors/__init__.py +120 -0
  4. skill_seekers/cli/adaptors/base.py +221 -0
  5. skill_seekers/cli/adaptors/claude.py +485 -0
  6. skill_seekers/cli/adaptors/gemini.py +453 -0
  7. skill_seekers/cli/adaptors/markdown.py +269 -0
  8. skill_seekers/cli/adaptors/openai.py +503 -0
  9. skill_seekers/cli/ai_enhancer.py +310 -0
  10. skill_seekers/cli/api_reference_builder.py +373 -0
  11. skill_seekers/cli/architectural_pattern_detector.py +525 -0
  12. skill_seekers/cli/code_analyzer.py +1462 -0
  13. skill_seekers/cli/codebase_scraper.py +1225 -0
  14. skill_seekers/cli/config_command.py +563 -0
  15. skill_seekers/cli/config_enhancer.py +431 -0
  16. skill_seekers/cli/config_extractor.py +871 -0
  17. skill_seekers/cli/config_manager.py +452 -0
  18. skill_seekers/cli/config_validator.py +394 -0
  19. skill_seekers/cli/conflict_detector.py +528 -0
  20. skill_seekers/cli/constants.py +72 -0
  21. skill_seekers/cli/dependency_analyzer.py +757 -0
  22. skill_seekers/cli/doc_scraper.py +2332 -0
  23. skill_seekers/cli/enhance_skill.py +488 -0
  24. skill_seekers/cli/enhance_skill_local.py +1096 -0
  25. skill_seekers/cli/enhance_status.py +194 -0
  26. skill_seekers/cli/estimate_pages.py +433 -0
  27. skill_seekers/cli/generate_router.py +1209 -0
  28. skill_seekers/cli/github_fetcher.py +534 -0
  29. skill_seekers/cli/github_scraper.py +1466 -0
  30. skill_seekers/cli/guide_enhancer.py +723 -0
  31. skill_seekers/cli/how_to_guide_builder.py +1267 -0
  32. skill_seekers/cli/install_agent.py +461 -0
  33. skill_seekers/cli/install_skill.py +178 -0
  34. skill_seekers/cli/language_detector.py +614 -0
  35. skill_seekers/cli/llms_txt_detector.py +60 -0
  36. skill_seekers/cli/llms_txt_downloader.py +104 -0
  37. skill_seekers/cli/llms_txt_parser.py +150 -0
  38. skill_seekers/cli/main.py +558 -0
  39. skill_seekers/cli/markdown_cleaner.py +132 -0
  40. skill_seekers/cli/merge_sources.py +806 -0
  41. skill_seekers/cli/package_multi.py +77 -0
  42. skill_seekers/cli/package_skill.py +241 -0
  43. skill_seekers/cli/pattern_recognizer.py +1825 -0
  44. skill_seekers/cli/pdf_extractor_poc.py +1166 -0
  45. skill_seekers/cli/pdf_scraper.py +617 -0
  46. skill_seekers/cli/quality_checker.py +519 -0
  47. skill_seekers/cli/rate_limit_handler.py +438 -0
  48. skill_seekers/cli/resume_command.py +160 -0
  49. skill_seekers/cli/run_tests.py +230 -0
  50. skill_seekers/cli/setup_wizard.py +93 -0
  51. skill_seekers/cli/split_config.py +390 -0
  52. skill_seekers/cli/swift_patterns.py +560 -0
  53. skill_seekers/cli/test_example_extractor.py +1081 -0
  54. skill_seekers/cli/test_unified_simple.py +179 -0
  55. skill_seekers/cli/unified_codebase_analyzer.py +572 -0
  56. skill_seekers/cli/unified_scraper.py +932 -0
  57. skill_seekers/cli/unified_skill_builder.py +1605 -0
  58. skill_seekers/cli/upload_skill.py +162 -0
  59. skill_seekers/cli/utils.py +432 -0
  60. skill_seekers/mcp/__init__.py +33 -0
  61. skill_seekers/mcp/agent_detector.py +316 -0
  62. skill_seekers/mcp/git_repo.py +273 -0
  63. skill_seekers/mcp/server.py +231 -0
  64. skill_seekers/mcp/server_fastmcp.py +1249 -0
  65. skill_seekers/mcp/server_legacy.py +2302 -0
  66. skill_seekers/mcp/source_manager.py +285 -0
  67. skill_seekers/mcp/tools/__init__.py +115 -0
  68. skill_seekers/mcp/tools/config_tools.py +251 -0
  69. skill_seekers/mcp/tools/packaging_tools.py +826 -0
  70. skill_seekers/mcp/tools/scraping_tools.py +842 -0
  71. skill_seekers/mcp/tools/source_tools.py +828 -0
  72. skill_seekers/mcp/tools/splitting_tools.py +212 -0
  73. skill_seekers/py.typed +0 -0
  74. skill_seekers-2.7.3.dist-info/METADATA +2027 -0
  75. skill_seekers-2.7.3.dist-info/RECORD +79 -0
  76. skill_seekers-2.7.3.dist-info/WHEEL +5 -0
  77. skill_seekers-2.7.3.dist-info/entry_points.txt +19 -0
  78. skill_seekers-2.7.3.dist-info/licenses/LICENSE +21 -0
  79. 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