opencode-skills-collection 3.0.37 → 3.0.39

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 (73) hide show
  1. package/bundled-skills/.antigravity-install-manifest.json +13 -1
  2. package/bundled-skills/2slides-ppt-generator/SKILL.md +786 -0
  3. package/bundled-skills/2slides-ppt-generator/references/api-reference.md +499 -0
  4. package/bundled-skills/2slides-ppt-generator/references/mcp-integration.md +282 -0
  5. package/bundled-skills/2slides-ppt-generator/references/pricing.md +195 -0
  6. package/bundled-skills/2slides-ppt-generator/scripts/api_constants.py +87 -0
  7. package/bundled-skills/2slides-ppt-generator/scripts/create_pdf_slides.py +159 -0
  8. package/bundled-skills/2slides-ppt-generator/scripts/download_slides_pages_voices.py +157 -0
  9. package/bundled-skills/2slides-ppt-generator/scripts/generate_narration.py +197 -0
  10. package/bundled-skills/2slides-ppt-generator/scripts/generate_slides.py +247 -0
  11. package/bundled-skills/2slides-ppt-generator/scripts/get_job_status.py +106 -0
  12. package/bundled-skills/2slides-ppt-generator/scripts/search_themes.py +137 -0
  13. package/bundled-skills/anti-sycophancy/README.md +86 -0
  14. package/bundled-skills/anti-sycophancy/SKILL.md +40 -0
  15. package/bundled-skills/antigravity-agent-manager/SKILL.md +112 -0
  16. package/bundled-skills/docs/integrations/jetski-cortex.md +3 -3
  17. package/bundled-skills/docs/integrations/jetski-gemini-loader/README.md +1 -1
  18. package/bundled-skills/docs/maintainers/repo-growth-seo.md +3 -3
  19. package/bundled-skills/docs/maintainers/skills-update-guide.md +1 -1
  20. package/bundled-skills/docs/sources/sources.md +1 -0
  21. package/bundled-skills/docs/users/bundles.md +1 -1
  22. package/bundled-skills/docs/users/claude-code-skills.md +1 -1
  23. package/bundled-skills/docs/users/gemini-cli-skills.md +1 -1
  24. package/bundled-skills/docs/users/getting-started.md +1 -1
  25. package/bundled-skills/docs/users/kiro-integration.md +1 -1
  26. package/bundled-skills/docs/users/usage.md +4 -4
  27. package/bundled-skills/docs/users/visual-guide.md +4 -4
  28. package/bundled-skills/event-staffing-compliance/SKILL.md +91 -0
  29. package/bundled-skills/event-staffing-ordering/SKILL.md +119 -0
  30. package/bundled-skills/examprep-ai/SKILL.md +446 -0
  31. package/bundled-skills/hasdata/SKILL.md +107 -0
  32. package/bundled-skills/hasdata/references/code-recipes.md +150 -0
  33. package/bundled-skills/hasdata/references/ecommerce.md +116 -0
  34. package/bundled-skills/hasdata/references/jobs.md +111 -0
  35. package/bundled-skills/hasdata/references/local-business.md +145 -0
  36. package/bundled-skills/hasdata/references/real-estate.md +84 -0
  37. package/bundled-skills/hasdata/references/scraper-jobs.md +252 -0
  38. package/bundled-skills/hasdata/references/search.md +154 -0
  39. package/bundled-skills/hasdata/references/travel.md +202 -0
  40. package/bundled-skills/hasdata/references/web-scraping.md +159 -0
  41. package/bundled-skills/hasdata/references/youtube.md +186 -0
  42. package/bundled-skills/hasdata-cli/SKILL.md +169 -0
  43. package/bundled-skills/hasdata-cli/references/all-commands.md +107 -0
  44. package/bundled-skills/hasdata-cli/references/ecommerce.md +106 -0
  45. package/bundled-skills/hasdata-cli/references/enrichment.md +227 -0
  46. package/bundled-skills/hasdata-cli/references/jobs.md +84 -0
  47. package/bundled-skills/hasdata-cli/references/local-business.md +123 -0
  48. package/bundled-skills/hasdata-cli/references/real-estate.md +126 -0
  49. package/bundled-skills/hasdata-cli/references/search.md +122 -0
  50. package/bundled-skills/hasdata-cli/references/travel.md +102 -0
  51. package/bundled-skills/hasdata-cli/references/web-scraping.md +181 -0
  52. package/bundled-skills/hasdata-cli/references/youtube.md +145 -0
  53. package/bundled-skills/linkedin-content-generator/SKILL.md +492 -0
  54. package/bundled-skills/linkedin-content-generator/scripts/generate_calendar.py +82 -0
  55. package/bundled-skills/linkedin-content-generator/scripts/generate_carousel.py +69 -0
  56. package/bundled-skills/linkedin-content-generator/scripts/generate_newsletter.py +64 -0
  57. package/bundled-skills/linkedin-content-generator/scripts/generate_post.py +77 -0
  58. package/bundled-skills/linkedin-content-generator/scripts/memory.md +49 -0
  59. package/bundled-skills/linkedin-content-generator/scripts/memory_manager.py +134 -0
  60. package/bundled-skills/linkedin-content-generator/scripts/utils.py +96 -0
  61. package/bundled-skills/permission-manager/README.md +22 -0
  62. package/bundled-skills/permission-manager/SKILL.md +54 -0
  63. package/bundled-skills/skill-suggester/README.md +14 -0
  64. package/bundled-skills/skill-suggester/SKILL.md +69 -0
  65. package/bundled-skills/smart-git-automation/README.md +31 -0
  66. package/bundled-skills/smart-git-automation/SKILL.md +96 -0
  67. package/bundled-skills/vercel-optimize/lib/cost-coverage.mjs +3 -1
  68. package/bundled-skills/vercel-optimize/lib/render-report.mjs +2 -2
  69. package/bundled-skills/vercel-optimize/lib/util.mjs +7 -0
  70. package/bundled-skills/vercel-optimize/lib/verify-claim.mjs +2 -7
  71. package/bundled-skills/vercel-optimize/lib/workspace-resolver.mjs +2 -1
  72. package/package.json +1 -1
  73. package/skills_index.json +268 -0
@@ -0,0 +1,159 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Generate custom-designed slides from text using the 2slides API.
4
+ Similar to create-like-this but without needing a reference image.
5
+ """
6
+
7
+ import os
8
+ import sys
9
+ import json
10
+ import argparse
11
+ import requests
12
+ from typing import Optional, Dict, Any
13
+
14
+
15
+ API_BASE_URL = "https://2slides.com/api/v1"
16
+
17
+
18
+ def get_api_key() -> str:
19
+ """Get API key from environment variable."""
20
+ api_key = os.environ.get("SLIDES_2SLIDES_API_KEY")
21
+ if not api_key:
22
+ raise ValueError(
23
+ "API key not found. Set SLIDES_2SLIDES_API_KEY environment variable.\n"
24
+ "Get your API key from: https://2slides.com/api"
25
+ )
26
+ return api_key
27
+
28
+
29
+ def create_pdf_slides(
30
+ user_input: str,
31
+ response_language: str = "Auto",
32
+ aspect_ratio: str = "16:9",
33
+ resolution: str = "2K",
34
+ page: int = 1,
35
+ content_detail: str = "concise",
36
+ design_spec: Optional[str] = None,
37
+ api_key: Optional[str] = None
38
+ ) -> Dict[str, Any]:
39
+ """
40
+ Generate custom-designed slides from text with optional design specifications.
41
+
42
+ Args:
43
+ user_input: Content to convert into slides
44
+ response_language: Language (default: "Auto")
45
+ Options: Auto, English, Simplified Chinese, Traditional Chinese, Spanish,
46
+ Arabic, Portuguese, Indonesian, Japanese, Russian, Hindi, French, German,
47
+ Vietnamese, Turkish, Polish, Italian, Korean
48
+ aspect_ratio: Aspect ratio in width:height format (default: "16:9")
49
+ resolution: Output quality - "1K", "2K", or "4K" (default: "2K")
50
+ page: Number of slides, 0 for auto-detection, max 100 (default: 1)
51
+ content_detail: "concise" (brief) or "standard" (detailed) (default: "concise")
52
+ design_spec: Optional design specifications (e.g., "modern minimalist", "corporate blue")
53
+ api_key: API key (uses env var if not provided)
54
+
55
+ Returns:
56
+ Dict with generation result
57
+ """
58
+ if api_key is None:
59
+ api_key = get_api_key()
60
+
61
+ headers = {
62
+ "Authorization": f"Bearer {api_key}",
63
+ "Content-Type": "application/json"
64
+ }
65
+
66
+ payload = {
67
+ "userInput": user_input,
68
+ "responseLanguage": response_language,
69
+ "aspectRatio": aspect_ratio,
70
+ "resolution": resolution,
71
+ "page": page,
72
+ "contentDetail": content_detail
73
+ }
74
+
75
+ if design_spec:
76
+ payload["designSpec"] = design_spec
77
+
78
+ url = f"{API_BASE_URL}/slides/create-pdf-slides"
79
+
80
+ # Calculate dynamic timeout: ~30s per page, minimum 120s
81
+ timeout = max(120, page * 40)
82
+
83
+ print("Generating custom-designed slides...", file=sys.stderr)
84
+ print(f"(Timeout set to {timeout}s for {page} page(s))", file=sys.stderr)
85
+ response = requests.post(url, headers=headers, json=payload, timeout=timeout)
86
+ response.raise_for_status()
87
+
88
+ result = response.json()
89
+
90
+ # Handle the actual API response structure
91
+ if result.get("success") and "data" in result:
92
+ data = result["data"]
93
+ # Transform to expected format for consistency
94
+ normalized_result = {
95
+ "slideUrl": data.get("jobUrl"),
96
+ "pdfUrl": data.get("downloadUrl"),
97
+ "status": "completed" if data.get("status") == "success" else data.get("status"),
98
+ "message": data.get("message"),
99
+ "slidePageCount": data.get("slidePageCount"),
100
+ "jobId": data.get("jobId")
101
+ }
102
+ print("✓ Slides generated successfully!", file=sys.stderr)
103
+ print(f" Pages: {data.get('slidePageCount')}", file=sys.stderr)
104
+ return normalized_result
105
+ else:
106
+ # Fallback to raw result if structure is unexpected
107
+ print("✓ Request completed!", file=sys.stderr)
108
+ return result
109
+
110
+
111
+ def main():
112
+ parser = argparse.ArgumentParser(
113
+ description="Generate custom-designed slides using 2slides API",
114
+ formatter_class=argparse.RawDescriptionHelpFormatter,
115
+ epilog="""
116
+ Examples:
117
+ # Generate slides with auto design
118
+ %(prog)s --content "Sales Report Q4 2025"
119
+
120
+ # Generate with specific design
121
+ %(prog)s --content "Marketing Plan" --design-spec "modern minimalist, blue color scheme"
122
+
123
+ # Generate in 4K resolution
124
+ %(prog)s --content "Product Launch" --resolution 4K --page 5
125
+ """
126
+ )
127
+
128
+ parser.add_argument("--content", required=True, help="Content for slides")
129
+ parser.add_argument("--design-spec", help="Optional design specifications")
130
+ parser.add_argument("--language", default="Auto", help="Response language (default: Auto)")
131
+ parser.add_argument("--aspect-ratio", default="16:9", help="Aspect ratio in width:height format (default: 16:9)")
132
+ parser.add_argument("--resolution", choices=["1K", "2K", "4K"], default="2K",
133
+ help="Output quality (default: 2K)")
134
+ parser.add_argument("--page", type=int, default=1, help="Number of slides, 0 for auto (default: 1, max: 100)")
135
+ parser.add_argument("--content-detail", choices=["concise", "standard"], default="concise",
136
+ help="Content detail level (default: concise)")
137
+
138
+ args = parser.parse_args()
139
+
140
+ try:
141
+ result = create_pdf_slides(
142
+ user_input=args.content,
143
+ response_language=args.language,
144
+ aspect_ratio=args.aspect_ratio,
145
+ resolution=args.resolution,
146
+ page=args.page,
147
+ content_detail=args.content_detail,
148
+ design_spec=args.design_spec
149
+ )
150
+
151
+ print(json.dumps(result, indent=2))
152
+
153
+ except Exception as e:
154
+ print(f"Error: {e}", file=sys.stderr)
155
+ sys.exit(1)
156
+
157
+
158
+ if __name__ == "__main__":
159
+ main()
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Download slides pages as PNG files and voice narrations as WAV files.
4
+ Exports everything as a ZIP archive (completely free).
5
+ """
6
+
7
+ import os
8
+ import sys
9
+ import json
10
+ import argparse
11
+ import requests
12
+ from typing import Optional, Dict, Any
13
+
14
+
15
+ API_BASE_URL = "https://2slides.com/api/v1"
16
+
17
+
18
+ def get_api_key() -> str:
19
+ """Get API key from environment variable."""
20
+ api_key = os.environ.get("SLIDES_2SLIDES_API_KEY")
21
+ if not api_key:
22
+ raise ValueError(
23
+ "API key not found. Set SLIDES_2SLIDES_API_KEY environment variable.\n"
24
+ "Get your API key from: https://2slides.com/api"
25
+ )
26
+ return api_key
27
+
28
+
29
+ def download_slides_pages_voices(
30
+ job_id: str,
31
+ output_path: Optional[str] = None,
32
+ api_key: Optional[str] = None
33
+ ) -> str:
34
+ """
35
+ Download slides pages and voice narrations as a ZIP archive.
36
+
37
+ Args:
38
+ job_id: Job ID from slide generation
39
+ output_path: Optional path to save the ZIP file (default: <job_id>.zip)
40
+ api_key: API key (uses env var if not provided)
41
+
42
+ Returns:
43
+ Path to the downloaded ZIP file
44
+
45
+ Notes:
46
+ - Exports pages as PNG files
47
+ - Exports voices as WAV files
48
+ - Includes transcripts
49
+ - Completely free (no credit cost)
50
+ - Download URL valid for 1 hour
51
+ """
52
+ if api_key is None:
53
+ api_key = get_api_key()
54
+
55
+ headers = {
56
+ "Authorization": f"Bearer {api_key}",
57
+ "Content-Type": "application/json"
58
+ }
59
+
60
+ payload = {
61
+ "jobId": job_id
62
+ }
63
+
64
+ url = f"{API_BASE_URL}/slides/download-slides-pages-voices"
65
+
66
+ print(f"Requesting download for job: {job_id}...", file=sys.stderr)
67
+
68
+ response = requests.post(url, headers=headers, json=payload, timeout=30)
69
+ response.raise_for_status()
70
+
71
+ result = response.json()
72
+
73
+ # Check API response structure
74
+ if not result.get("success"):
75
+ error_msg = result.get("error", "Unknown error")
76
+ raise ValueError(f"API error: {error_msg}")
77
+
78
+ # Get download URL from data field
79
+ data = result.get("data")
80
+ if not data:
81
+ raise ValueError("No data in API response")
82
+
83
+ download_url = data.get("downloadUrl")
84
+ if not download_url:
85
+ raise ValueError("No download URL in response")
86
+
87
+ # Optional: log additional info
88
+ file_name = data.get("fileName", "unknown.zip")
89
+ expires_in = data.get("expiresIn", 3600)
90
+ print(f" Filename: {file_name}", file=sys.stderr)
91
+ print(f" Expires in: {expires_in} seconds", file=sys.stderr)
92
+
93
+ # Download the ZIP file
94
+ if output_path is None:
95
+ output_path = f"{job_id}.zip"
96
+
97
+ print(f"Downloading ZIP archive to: {output_path}...", file=sys.stderr)
98
+
99
+ zip_response = requests.get(download_url, stream=True, timeout=120)
100
+ zip_response.raise_for_status()
101
+
102
+ # Save to file
103
+ with open(output_path, 'wb') as f:
104
+ for chunk in zip_response.iter_content(chunk_size=8192):
105
+ f.write(chunk)
106
+
107
+ file_size = os.path.getsize(output_path)
108
+ print(f"✓ Downloaded successfully!", file=sys.stderr)
109
+ print(f" File: {output_path}", file=sys.stderr)
110
+ print(f" Size: {file_size:,} bytes", file=sys.stderr)
111
+
112
+ return output_path
113
+
114
+
115
+ def main():
116
+ parser = argparse.ArgumentParser(
117
+ description="Download 2slides pages and voices as ZIP archive (FREE)",
118
+ formatter_class=argparse.RawDescriptionHelpFormatter,
119
+ epilog="""
120
+ Examples:
121
+ # Download with default filename
122
+ %(prog)s --job-id "abc-123-def-456"
123
+
124
+ # Download to specific path
125
+ %(prog)s --job-id "abc-123-def-456" --output slides.zip
126
+
127
+ Archive Contents:
128
+ - Pages as PNG files
129
+ - Voice files as WAV
130
+ - Transcripts
131
+
132
+ Note: Download URLs are valid for 1 hour only
133
+ Cost: Completely FREE (no credits used)
134
+ """
135
+ )
136
+
137
+ parser.add_argument("--job-id", required=True, help="Job ID from slide generation")
138
+ parser.add_argument("--output", help="Output ZIP file path (default: <job_id>.zip)")
139
+
140
+ args = parser.parse_args()
141
+
142
+ try:
143
+ output_path = download_slides_pages_voices(
144
+ job_id=args.job_id,
145
+ output_path=args.output
146
+ )
147
+
148
+ # Output path for easy parsing
149
+ print(json.dumps({"success": True, "output": output_path}, indent=2))
150
+
151
+ except Exception as e:
152
+ print(f"Error: {e}", file=sys.stderr)
153
+ sys.exit(1)
154
+
155
+
156
+ if __name__ == "__main__":
157
+ main()
@@ -0,0 +1,197 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Generate AI voice narration for slides using the 2slides API.
4
+ Supports single and multi-speaker modes with 30 voice options.
5
+ """
6
+
7
+ import os
8
+ import sys
9
+ import json
10
+ import argparse
11
+ import requests
12
+ from typing import Optional, Dict, Any, List
13
+
14
+
15
+ API_BASE_URL = "https://2slides.com/api/v1"
16
+
17
+ # Available voice options (30 voices)
18
+ AVAILABLE_VOICES = [
19
+ "Puck", "Aoede", "Charon", "Kore", "Fenrir", "Phoebe", "Asteria",
20
+ "Luna", "Stella", "Theia", "Helios", "Atlas", "Clio", "Melpomene",
21
+ "Calliope", "Erato", "Euterpe", "Polyhymnia", "Terpsichore", "Thalia",
22
+ "Urania", "Zeus", "Hera", "Poseidon", "Athena", "Apollo", "Artemis",
23
+ "Ares", "Aphrodite", "Hephaestus"
24
+ ]
25
+
26
+
27
+ def get_api_key() -> str:
28
+ """Get API key from environment variable."""
29
+ api_key = os.environ.get("SLIDES_2SLIDES_API_KEY")
30
+ if not api_key:
31
+ raise ValueError(
32
+ "API key not found. Set SLIDES_2SLIDES_API_KEY environment variable.\n"
33
+ "Get your API key from: https://2slides.com/api"
34
+ )
35
+ return api_key
36
+
37
+
38
+ def generate_narration(
39
+ job_id: str,
40
+ language: str = "Auto",
41
+ voice: str = "Puck",
42
+ multi_speaker: bool = False,
43
+ api_key: Optional[str] = None
44
+ ) -> Dict[str, Any]:
45
+ """
46
+ Generate AI voice narration for slides.
47
+
48
+ Args:
49
+ job_id: Job ID from slide generation (must be UUID format for Nano Banana)
50
+ language: Language for narration (default: "Auto")
51
+ Options: Auto, English, Simplified Chinese, Traditional Chinese, Spanish,
52
+ Arabic, Portuguese, Indonesian, Japanese, Russian, Hindi, French, German,
53
+ Vietnamese, Turkish, Polish, Italian, Korean
54
+ voice: Voice name (default: "Puck")
55
+ Options: Puck, Aoede, Charon, Kore, Fenrir, Phoebe, Asteria, Luna, Stella,
56
+ Theia, Helios, Atlas, Clio, Melpomene, Calliope, Erato, Euterpe, Polyhymnia,
57
+ Terpsichore, Thalia, Urania, Zeus, Hera, Poseidon, Athena, Apollo, Artemis,
58
+ Ares, Aphrodite, Hephaestus
59
+ multi_speaker: Enable multi-speaker mode (default: False)
60
+ api_key: API key (uses env var if not provided)
61
+
62
+ Returns:
63
+ Dict with narration generation result
64
+
65
+ Notes:
66
+ - Job must be completed before adding narration
67
+ - Cost: 210 credits per page (10 for text, 200 for audio)
68
+ - Processing time: Varies by slide count
69
+ """
70
+ if api_key is None:
71
+ api_key = get_api_key()
72
+
73
+ if voice not in AVAILABLE_VOICES:
74
+ print(f"Warning: Voice '{voice}' not in known voices list", file=sys.stderr)
75
+ print(f"Available voices: {', '.join(AVAILABLE_VOICES)}", file=sys.stderr)
76
+
77
+ headers = {
78
+ "Authorization": f"Bearer {api_key}",
79
+ "Content-Type": "application/json"
80
+ }
81
+
82
+ payload = {
83
+ "jobId": job_id,
84
+ "language": language,
85
+ "voice": voice,
86
+ "multiSpeaker": multi_speaker
87
+ }
88
+
89
+ url = f"{API_BASE_URL}/slides/generate-narration"
90
+
91
+ print("Generating voice narration...", file=sys.stderr)
92
+ print(f"Voice: {voice}, Multi-speaker: {multi_speaker}", file=sys.stderr)
93
+
94
+ # Set reasonable timeout for narration generation
95
+ timeout = 120
96
+
97
+ response = requests.post(url, headers=headers, json=payload, timeout=timeout)
98
+ response.raise_for_status()
99
+
100
+ result = response.json()
101
+
102
+ # Check API response structure
103
+ if not result.get("success"):
104
+ # Common error example:
105
+ # {"error":"Job is not completed","code":"JOB_NOT_COMPLETED",...}
106
+ error_msg = result.get("error", "Unknown error")
107
+ code = result.get("code")
108
+ details = result.get("details")
109
+ extra = f" (code={code})" if code else ""
110
+ raise ValueError(f"API error: {error_msg}{extra}{f' details={details}' if details else ''}")
111
+
112
+ # API may return either:
113
+ # - { success:true, data:{...} }
114
+ # - { success:true, jobId:"...", message:"..." } (no data field)
115
+ data = result.get("data")
116
+ if not data:
117
+ data = {
118
+ "jobId": result.get("jobId") or job_id,
119
+ "status": result.get("status") or "pending",
120
+ "message": result.get("message") or "Narration generation started"
121
+ }
122
+
123
+ print("✓ Narration generation started!", file=sys.stderr)
124
+ print(f" Job ID: {data.get('jobId')}", file=sys.stderr)
125
+ print("Use get_job_status.py to check progress", file=sys.stderr)
126
+
127
+ return data
128
+
129
+
130
+ def list_voices():
131
+ """Print available voices."""
132
+ print("Available voices (30 total):")
133
+ print("-" * 40)
134
+ for i, voice in enumerate(AVAILABLE_VOICES, 1):
135
+ print(f"{i:2d}. {voice}")
136
+ print("-" * 40)
137
+ print("\nPopular choices: Puck, Aoede, Charon")
138
+
139
+
140
+ def main():
141
+ parser = argparse.ArgumentParser(
142
+ description="Generate AI voice narration for 2slides presentations",
143
+ formatter_class=argparse.RawDescriptionHelpFormatter,
144
+ epilog="""
145
+ Examples:
146
+ # List available voices
147
+ %(prog)s --list-voices
148
+
149
+ # Generate narration with default voice
150
+ %(prog)s --job-id "abc-123-def-456"
151
+
152
+ # Generate with specific voice
153
+ %(prog)s --job-id "abc-123-def-456" --voice "Aoede"
154
+
155
+ # Generate with multi-speaker mode
156
+ %(prog)s --job-id "abc-123-def-456" --multi-speaker
157
+
158
+ # Generate in Spanish
159
+ %(prog)s --job-id "abc-123-def-456" --language "Spanish" --voice "Charon"
160
+
161
+ Credit Cost: 210 credits per page (10 for text, 200 for audio)
162
+ """
163
+ )
164
+
165
+ parser.add_argument("--job-id", help="Job ID from slide generation (UUID format)")
166
+ parser.add_argument("--language", default="Auto", help="Narration language (default: Auto)")
167
+ parser.add_argument("--voice", default="Puck", help="Voice name (default: Puck)")
168
+ parser.add_argument("--multi-speaker", action="store_true", help="Enable multi-speaker mode")
169
+ parser.add_argument("--list-voices", action="store_true", help="List available voices and exit")
170
+
171
+ args = parser.parse_args()
172
+
173
+ if args.list_voices:
174
+ list_voices()
175
+ return
176
+
177
+ if not args.job_id:
178
+ print("Error: --job-id is required (or use --list-voices)", file=sys.stderr)
179
+ sys.exit(1)
180
+
181
+ try:
182
+ result = generate_narration(
183
+ job_id=args.job_id,
184
+ language=args.language,
185
+ voice=args.voice,
186
+ multi_speaker=args.multi_speaker
187
+ )
188
+
189
+ print(json.dumps(result, indent=2))
190
+
191
+ except Exception as e:
192
+ print(f"Error: {e}", file=sys.stderr)
193
+ sys.exit(1)
194
+
195
+
196
+ if __name__ == "__main__":
197
+ main()