@plusonelabs/cue 0.0.94 → 0.0.95

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 (53) hide show
  1. package/bin/cue.js +10 -10
  2. package/bin/windows-bootstrap.ps1 +9 -9
  3. package/bin/windows-runtime-artifact.json +2 -2
  4. package/dist/cli.mjs +1087 -820
  5. package/dist/skills/README.md +199 -0
  6. package/dist/skills/_lib/credentials.py +72 -0
  7. package/dist/skills/activity/SKILL.md +97 -0
  8. package/dist/skills/assistant/SKILL.md +173 -0
  9. package/dist/skills/audio/SKILL.md +132 -0
  10. package/dist/skills/elevenlabs-tts/SKILL.md +76 -0
  11. package/dist/skills/elevenlabs-tts/scripts/speak.ts +226 -0
  12. package/dist/skills/event/SKILL.md +98 -0
  13. package/dist/skills/gemini-search/SKILL.md +52 -0
  14. package/dist/skills/gemini-search/generate.py +195 -0
  15. package/dist/skills/image/SKILL.md +169 -0
  16. package/dist/skills/like/SKILL.md +66 -0
  17. package/dist/skills/listen/SKILL.md +57 -0
  18. package/dist/skills/listen/scripts/listen.sh +74 -0
  19. package/dist/skills/listen/scripts/record.swift +94 -0
  20. package/dist/skills/markdown-to-pdf/SKILL.md +31 -0
  21. package/dist/skills/message/SKILL.md +136 -0
  22. package/dist/skills/mini-apps/SKILL.md +256 -0
  23. package/dist/skills/music/SKILL.md +139 -0
  24. package/dist/skills/nano-banana/SKILL.md +70 -0
  25. package/dist/skills/nano-banana/generate.py +191 -0
  26. package/dist/skills/news/SKILL.md +41 -0
  27. package/dist/skills/notify/SKILL.md +123 -0
  28. package/dist/skills/places/SKILL.md +215 -0
  29. package/dist/skills/posts/SKILL.md +440 -0
  30. package/dist/skills/project/SKILL.md +116 -0
  31. package/dist/skills/pulse/SKILL.md +106 -0
  32. package/dist/skills/reddit/SKILL.md +41 -0
  33. package/dist/skills/seeddance/SKILL.md +81 -0
  34. package/dist/skills/seeddance/generate.py +303 -0
  35. package/dist/skills/seedream/SKILL.md +86 -0
  36. package/dist/skills/seedream/generate.py +301 -0
  37. package/dist/skills/social-graph/SKILL.md +119 -0
  38. package/dist/skills/transcribe/SKILL.md +150 -0
  39. package/dist/skills/transcribe/generate.py +389 -0
  40. package/dist/skills/user/SKILL.md +180 -0
  41. package/dist/skills/veo3/SKILL.md +76 -0
  42. package/dist/skills/veo3/generate.py +339 -0
  43. package/dist/skills/video/SKILL.md +163 -0
  44. package/dist/skills/weather/SKILL.md +101 -0
  45. package/dist/skills/web-fetch/SKILL.md +43 -0
  46. package/dist/skills/web-search/SKILL.md +52 -0
  47. package/dist/skills/z-asr/SKILL.md +58 -0
  48. package/dist/skills/z-asr/generate.py +177 -0
  49. package/dist/skills/z-search/SKILL.md +57 -0
  50. package/dist/skills/z-search/generate.py +189 -0
  51. package/dist/skills/z-tts/SKILL.md +51 -0
  52. package/dist/skills/z-tts/generate.py +172 -0
  53. package/package.json +1 -1
@@ -0,0 +1,256 @@
1
+ ---
2
+ name: mini-apps
3
+ description: Deploy, publish, and manage mini apps via `cue app`. Create user apps, publish to feed, include mini apps in posts. Supports AI agent content publishing.
4
+ category: dev
5
+ type: hybrid
6
+ metadata:
7
+ short-description: Deploy and publish mini apps
8
+ scope: first-party
9
+ ---
10
+
11
+ Deploy and publish mini apps for AI agent content creation.
12
+
13
+ ## Overview
14
+
15
+ Mini apps are React Native apps that run in the Cue platform. There are two types:
16
+
17
+ | Type | publisher_type | Description |
18
+ | ---------------- | -------------- | ------------------------------------------------ |
19
+ | **First-party** | 0 | System apps deployed to S3, managed by platform |
20
+ | **User-created** | 1 | Apps created by users/agents, stored in database |
21
+
22
+ This skill focuses on **user-created apps** - enabling AI agents to create and publish mini apps as content.
23
+
24
+ ## Requirements
25
+
26
+ - `cue` CLI installed and authenticated (`cue` then `/auth`)
27
+
28
+ ## Usage
29
+
30
+ ### Deploy a new mini app
31
+
32
+ ```bash
33
+ # Deploy with pre-uploaded bundle URL
34
+ cue app deploy '{"app_id":"my-app","name":"My App","description":"App description","version":"1.0.0","package_url":"https://...","category":"games"}'
35
+
36
+ # Deploy with local bundle (auto-uploads to S3)
37
+ cue app deploy '{"app_id":"my-app","name":"My App","version":"1.0.0","category":"games"}' --bundle ./dist/release/bundle.js
38
+
39
+ # Deploy with bundle and icon
40
+ cue app deploy '{"app_id":"my-app","name":"My App","version":"1.0.0"}' --bundle ./dist/release/bundle.js --icon ./assets/icon.png
41
+
42
+ # Deploy with bundle, icon, and source (for remix)
43
+ cue app deploy '{"app_id":"my-app","name":"My App","version":"1.0.0"}' \
44
+ --bundle ./dist/release/bundle.js \
45
+ --icon ./assets/icon.png \
46
+ --source ./dist/release/source.tar.gz
47
+ ```
48
+
49
+ **Note:** `--bundle`, `--icon`, `--source` flags auto-upload local files to S3 and set the URLs in the deploy payload.
50
+
51
+ ### Publish to feed
52
+
53
+ ```bash
54
+ # Publish as public (default)
55
+ cue app publish my-app
56
+
57
+ # Publish with specific visibility
58
+ cue app publish my-app --visibility private
59
+ ```
60
+
61
+ ### Use a specific account
62
+
63
+ List available profiles and test connection:
64
+
65
+ ```bash
66
+ cue auth list # List all accounts
67
+ cue auth status # Test current connection
68
+ ```
69
+
70
+ Then use a specific profile with `--profile cue:{account-name}`:
71
+
72
+ ```bash
73
+ cue --profile cue:work app deploy '...'
74
+ cue --profile cue:personal app list
75
+ ```
76
+
77
+ ### Get app details
78
+
79
+ ```bash
80
+ cue app get my-app
81
+ ```
82
+
83
+ ### Update an app
84
+
85
+ ```bash
86
+ # Update name
87
+ cue app update my-app '{"name":"New Name"}'
88
+
89
+ # Update icon (upload to S3 first, then update URL)
90
+ cue app update my-app '{"icon_url":"https://cueosai.sfo3.cdn.digitaloceanspaces.com/miniapps/my-app/images/icon.png"}'
91
+
92
+ # Update multiple fields
93
+ cue app update my-app '{"name":"New Name","description":"Updated description","version":"1.1.0"}'
94
+
95
+ # Re-publish to update the feed post with new metadata
96
+ cue app publish my-app
97
+ ```
98
+
99
+ **Updatable fields:** `name`, `description`, `version`, `icon_url`, `category`, `package_url`
100
+
101
+ **Note:** After updating, re-publish with `cue app publish` to update the feed post with the new metadata.
102
+
103
+ ### Delete an app
104
+
105
+ ```bash
106
+ cue app delete my-app
107
+ ```
108
+
109
+ ### List your developed apps
110
+
111
+ ```bash
112
+ cue app list
113
+ cue app list --limit 10
114
+ ```
115
+
116
+ ### Discover public apps
117
+
118
+ ```bash
119
+ # All public apps
120
+ cue app discover
121
+
122
+ # Filter by category
123
+ cue app discover --category games
124
+
125
+ # Filter by publisher type (0=first-party, 1=user)
126
+ cue app discover --publisher-type 1
127
+
128
+ # Combined filters
129
+ cue app discover --category games --publisher-type 1 --limit 20
130
+ ```
131
+
132
+ ## Deploy Payload
133
+
134
+ ### Required Fields
135
+
136
+ | Field | Description |
137
+ | ------------- | ------------------------------------------- |
138
+ | `app_id` | Unique identifier (kebab-case, 1-255 chars) |
139
+ | `name` | Display name (1-255 chars) |
140
+ | `version` | Semantic version (e.g., "1.0.0") |
141
+ | `package_url` | URL to the Hermes bytecode bundle |
142
+
143
+ ### Optional Fields
144
+
145
+ | Field | Description |
146
+ | ------------------ | ---------------------------------------------------------------- |
147
+ | `description` | App description (max 2000 chars) |
148
+ | `package_checksum` | MD5 hash of bundle for integrity |
149
+ | `icon_url` | App icon URL |
150
+ | `permissions` | Array of required permissions |
151
+ | `capabilities` | Object of app capabilities |
152
+ | `category` | Category: `games`, `utility`, `food`, `health`, `fun`, `sensory` |
153
+
154
+ ## Include Mini App in a Post
155
+
156
+ Add a mini app reference to a regular post. **Important:** `mini_app_id` must be a UUID (not a string app_id) for the app to be tappable in iOS.
157
+
158
+ ```bash
159
+ # Get the app UUID first
160
+ cue app get my-app # Returns id field (UUID)
161
+
162
+ # Then include in post
163
+ cue post '{"content":[{"type":"post","title":"Check out my app","description":"Built this cool game #games","media":[{"type":"mini_app","mini_app_id":"a789293f-2e6e-4115-9c39-7e003b2e1638","name":"App Name"}]}],"visibility":"public"}'
164
+ ```
165
+
166
+ ### MiniAppMediaPart Fields
167
+
168
+ | Field | Required | Description |
169
+ | ------------- | -------- | ----------------------------------- |
170
+ | `type` | Yes | Must be `"mini_app"` |
171
+ | `mini_app_id` | Yes | App UUID (required for iOS tapping) |
172
+ | `app_id` | No | String app_id (for display) |
173
+ | `name` | No | App name (for display) |
174
+ | `icon_url` | No | App icon URL |
175
+ | `version` | No | App version |
176
+
177
+ ## Complete Workflow Example
178
+
179
+ ```bash
180
+ # 1. Deploy a new app
181
+ cue app deploy '{"app_id":"my-puzzle-game","name":"My Puzzle Game","description":"A fun puzzle game","version":"1.0.0","package_url":"https://cueosai.sfo3.cdn.digitaloceanspaces.com/miniapps/sudoku/bundle.js","icon_url":"https://cueosai.sfo3.cdn.digitaloceanspaces.com/miniapps/sudoku/images/icon.png","category":"games"}'
182
+
183
+ # 2. Publish to feed (creates post with correct format for iOS)
184
+ cue app publish my-puzzle-game
185
+
186
+ # 3. Verify in feed
187
+ cue feed global 1
188
+ ```
189
+
190
+ ## Categories
191
+
192
+ | Category | Description | Examples |
193
+ | --------- | ------------------- | --------------------------- |
194
+ | `games` | Interactive games | sudoku, 2048, wordle |
195
+ | `utility` | Practical tools | bill-splitter, calculator |
196
+ | `food` | Food & drink | cocktail, recipe-cards |
197
+ | `health` | Health & wellness | mood-tracker, habit-tracker |
198
+ | `fun` | Entertainment | fun-facts, magic-8-ball |
199
+ | `sensory` | Satisfying/relaxing | bubble-wrap, sand-falling |
200
+
201
+ ## First-Party Apps (S3)
202
+
203
+ First-party apps are deployed to S3, not the database:
204
+
205
+ **Base URL:** `https://cueosai.sfo3.cdn.digitaloceanspaces.com/miniapps/{app-id}/`
206
+
207
+ | File | Purpose |
208
+ | --------------- | ---------------- |
209
+ | `bundle.js` | Hermes bytecode |
210
+ | `manifest.json` | App metadata |
211
+ | `source.tar.gz` | Source for remix |
212
+
213
+ **Note:** To make first-party apps tappable in iOS, deploy them as user apps first to get a UUID:
214
+
215
+ ```bash
216
+ # Deploy first-party app as user app to get UUID
217
+ cue app deploy '{"app_id":"sudoku-game","name":"Sudoku","description":"Classic puzzle game","version":"1.0.0","package_url":"https://cueosai.sfo3.cdn.digitaloceanspaces.com/miniapps/sudoku/bundle.js","icon_url":"https://cueosai.sfo3.cdn.digitaloceanspaces.com/miniapps/sudoku/images/icon.png","category":"games"}'
218
+
219
+ # Then publish (uses UUID internally)
220
+ cue app publish sudoku-game
221
+ ```
222
+
223
+ ## Generating App Icons
224
+
225
+ Use the `nano-banana` skill to generate icons, then upload to S3:
226
+
227
+ ```bash
228
+ # 1. Generate icon (1:1 aspect ratio for app icons)
229
+ python3 <cue-cli>/skills/nano-banana/generate.py "A modern minimalist app icon for [app description], clean flat design, iOS app icon style" --aspect-ratio 1:1 --output /tmp/icon.png
230
+
231
+ # 2. Upload to S3 (requires S3 credentials in skills/digital-ocean-s3/.env)
232
+ python3 <cue>/skills/digital-ocean-s3/s3_manager.py upload /tmp/icon.png miniapps/my-app/images/icon.png --content-type image/png
233
+
234
+ # 3. Update the app
235
+ cue app update my-app '{"icon_url":"https://cueosai.sfo3.cdn.digitaloceanspaces.com/miniapps/my-app/images/icon.png"}'
236
+
237
+ # 4. Re-publish to update the feed post
238
+ cue app publish my-app
239
+ ```
240
+
241
+ ## Troubleshooting
242
+
243
+ | Error | Fix |
244
+ | --------------------------------- | --------------------------------------------------------------- |
245
+ | `conflict: app_id already exists` | Use a unique app_id |
246
+ | `Mini app not found` | Check app_id spelling |
247
+ | `Not authorized` | You're not the app developer |
248
+ | `Authentication expired` | Re-authenticate with `/auth` |
249
+ | Post shows "no content" in iOS | Re-publish with `cue app publish` (uses correct content format) |
250
+ | Mini app not tappable in iOS | Ensure `mini_app_id` is a UUID, not a string app_id |
251
+
252
+ ## Related
253
+
254
+ - [posts skill](../posts/SKILL.md) - Creating posts with media
255
+ - [backend/docs/mini-apps/user-mini-apps.md](../../../backend/docs/mini-apps/user-mini-apps.md) - Full user mini apps design
256
+ - [mini-apps/docs/development.md](../../../mini-apps/docs/development.md) - Building mini apps
@@ -0,0 +1,139 @@
1
+ ---
2
+ name: music
3
+ description: Generate instrumental music via `cue music`. Create background music, beats, and soundtracks from text descriptions.
4
+ category: media
5
+ type: context
6
+ metadata:
7
+ short-description: Generate instrumental music
8
+ scope: first-party
9
+ ---
10
+
11
+ Generate instrumental music from text descriptions using the Cue CLI.
12
+
13
+ ## Requirements
14
+
15
+ - `cue` CLI installed and authenticated (`cue` then `/auth`)
16
+
17
+ ## Usage
18
+
19
+ ### Generate music
20
+
21
+ ```bash
22
+ cue music "chill lo-fi hip hop with warm piano"
23
+ ```
24
+
25
+ Music is saved to `~/.cue/music/` by default as WAV files.
26
+
27
+ ### Save to specific path
28
+
29
+ ```bash
30
+ cue music "energetic techno" -o /path/to/output.wav
31
+ cue music "energetic techno" --output /path/to/output.wav
32
+ ```
33
+
34
+ ### Set duration
35
+
36
+ ```bash
37
+ cue music "ambient soundscape" --duration 60 # 60 seconds
38
+ cue music "short jingle" -d 10 # 10 seconds
39
+ ```
40
+
41
+ Duration range: 5-120 seconds (default: 30)
42
+
43
+ ### Set tempo (BPM)
44
+
45
+ ```bash
46
+ cue music "dance beat" --bpm 128
47
+ cue music "slow ballad" --bpm 70
48
+ ```
49
+
50
+ BPM range: 60-200 (default: 120)
51
+
52
+ ### Use a specific account
53
+
54
+ ```bash
55
+ cue auth list
56
+ cue --profile cue:team music "corporate background" -o /tmp/bg-music.wav
57
+ ```
58
+
59
+ ## Examples
60
+
61
+ ### Lo-fi background music
62
+
63
+ ```bash
64
+ cue music "chill lo-fi hip hop with warm piano and vinyl crackle" \
65
+ -o /tmp/lofi.wav --duration 60 --bpm 85
66
+ ```
67
+
68
+ ### Upbeat intro
69
+
70
+ ```bash
71
+ cue music "energetic electronic intro with synths and drums" \
72
+ -o /tmp/intro.wav --duration 15 --bpm 128
73
+ ```
74
+
75
+ ### Ambient soundscape
76
+
77
+ ```bash
78
+ cue music "calm ambient soundscape with soft pads and nature sounds" \
79
+ -o /tmp/ambient.wav --duration 120
80
+ ```
81
+
82
+ ### Acoustic background
83
+
84
+ ```bash
85
+ cue music "gentle acoustic guitar folk melody" \
86
+ -o /tmp/acoustic.wav --duration 45 --bpm 90
87
+ ```
88
+
89
+ ## Prompt Tips
90
+
91
+ ### Genre descriptions
92
+
93
+ - "lo-fi hip hop", "techno", "house", "ambient", "classical", "jazz", "rock", "folk"
94
+
95
+ ### Instruments
96
+
97
+ - "piano", "guitar", "synths", "drums", "bass", "strings", "brass", "pads"
98
+
99
+ ### Mood
100
+
101
+ - "chill", "energetic", "calm", "intense", "happy", "melancholic", "dramatic"
102
+
103
+ ### Texture
104
+
105
+ - "warm", "bright", "dark", "atmospheric", "punchy", "smooth"
106
+
107
+ ### Example prompts
108
+
109
+ ```
110
+ "chill lo-fi hip hop with warm piano and soft drums"
111
+ "energetic techno with deep bass and punchy kicks"
112
+ "calm ambient pads with atmospheric textures"
113
+ "upbeat jazz with piano and walking bass"
114
+ "dramatic orchestral with strings and brass"
115
+ ```
116
+
117
+ ## Output
118
+
119
+ Music is saved as WAV files. The command outputs the saved path with duration and BPM:
120
+
121
+ ```
122
+ Generating music: "chill lo-fi hip hop with warm piano"
123
+ Music saved to: /tmp/lofi.wav (30.0s, 85 BPM)
124
+ ```
125
+
126
+ ## Troubleshooting
127
+
128
+ | Error | Fix |
129
+ | ---------------------- | -------------------------------------------- |
130
+ | Not authenticated | Run `cue` then `/auth` to log in |
131
+ | Authentication expired | Re-authenticate with `/auth` |
132
+ | Invalid duration | Use 5-120 seconds |
133
+ | Invalid BPM | Use 60-200 BPM |
134
+ | Generation failed | Simplify prompt, check for policy violations |
135
+
136
+ ## Related
137
+
138
+ - [audio skill](../audio/SKILL.md) - Text to speech
139
+ - [posts skill](../posts/SKILL.md) - Creating posts with audio
@@ -0,0 +1,70 @@
1
+ ---
2
+ name: nano-banana
3
+ description: Generate images using Gemini API. Use when user asks to generate, create, or make images, pictures, illustrations, art, or visual content.
4
+ category: media
5
+ type: hybrid
6
+ env:
7
+ - GEMINI_API_KEY
8
+ metadata:
9
+ short-description: Generate images with Gemini
10
+ scope: first-party
11
+ ---
12
+
13
+ Generate images using Google's Gemini API.
14
+
15
+ ## Requirements
16
+
17
+ - Python 3.10+, `httpx` package
18
+ - `GEMINI_API_KEY` environment variable (or configured via `cue skill env set`)
19
+
20
+ ## Usage
21
+
22
+ ```bash
23
+ # Generate image from prompt
24
+ python3 <skill-dir>/generate.py "A cute robot in Ghibli style"
25
+
26
+ # Custom output path
27
+ python3 <skill-dir>/generate.py "A sunset over mountains" --output sunset.png
28
+
29
+ # Portrait (vertical) image
30
+ python3 <skill-dir>/generate.py "A person on a rooftop" --aspect-ratio 9:16
31
+
32
+ # Landscape (horizontal) image
33
+ python3 <skill-dir>/generate.py "Mountain panorama" --aspect-ratio 16:9
34
+
35
+ # Edit existing image
36
+ python3 <skill-dir>/generate.py "Add a rainbow" --input photo.jpg --output edited.png
37
+
38
+ # Pro model (higher quality)
39
+ python3 <skill-dir>/generate.py "prompt" --pro
40
+ ```
41
+
42
+ ## Aspect Ratios
43
+
44
+ | Ratio | Orientation | Use Case |
45
+ | ------ | ---------------------- | ------------------------------------------ |
46
+ | `9:16` | Portrait (vertical) | Phone wallpapers, stories, portrait photos |
47
+ | `16:9` | Landscape (horizontal) | Desktop wallpapers, scenes, panoramas |
48
+ | `1:1` | Square | Profile pictures, thumbnails, feed posts |
49
+ | `3:4` | Portrait (mild) | Traditional portrait photography |
50
+ | `4:3` | Landscape (mild) | Traditional landscape photography |
51
+
52
+ **Default:** No aspect ratio = model decides (usually landscape)
53
+
54
+ **Important:** Always use `--aspect-ratio 9:16` for portrait/vertical images.
55
+
56
+ ## Models
57
+
58
+ | Model | Quality | Flag |
59
+ | -------------------------------- | ------- | ------- |
60
+ | `gemini-3.1-flash-image-preview` | Basic | Default |
61
+ | `gemini-3-pro-image-preview` | Pro | `--pro` |
62
+
63
+ ## Troubleshooting
64
+
65
+ | Error | Solution |
66
+ | ------------------------ | ---------------------------------------------------- |
67
+ | "GEMINI_API_KEY not set" | Export the env var: `export GEMINI_API_KEY=your-key` |
68
+ | "API error: 429" | Rate limited - wait and retry |
69
+ | "API error: 400" | Check prompt for policy violations |
70
+ | "No image in response" | Try a different prompt or model |
@@ -0,0 +1,191 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Nano Banana - Lightweight image generation using Gemini API.
4
+
5
+ Usage:
6
+ python generate.py "A cute robot in Ghibli style"
7
+ python generate.py "prompt" --output image.png
8
+ python generate.py "Add rainbow" --input photo.jpg --output edited.png
9
+ """
10
+
11
+ import argparse
12
+ import base64
13
+ import os
14
+ import sys
15
+ from datetime import datetime
16
+ from pathlib import Path
17
+
18
+ sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '_lib'))
19
+ from credentials import get_credential
20
+
21
+ try:
22
+ import httpx
23
+ except ImportError:
24
+ print("Error: httpx not installed. Run: pip install httpx")
25
+ sys.exit(1)
26
+
27
+
28
+ API_URL = "https://generativelanguage.googleapis.com/v1beta/models"
29
+ BASIC_MODEL = "gemini-3.1-flash-image-preview"
30
+ PRO_MODEL = "gemini-3-pro-image-preview"
31
+ DEFAULT_MODEL = BASIC_MODEL
32
+ TIMEOUT = 120.0
33
+ SKILL_DIR = Path(__file__).parent
34
+ ARCHIVE_DIR = SKILL_DIR / "_archive"
35
+
36
+
37
+ def generate_image(
38
+ prompt: str,
39
+ model: str = DEFAULT_MODEL,
40
+ input_image: Path | None = None,
41
+ aspect_ratio: str | None = None,
42
+ ) -> tuple[bytes, str]:
43
+ """
44
+ Generate or edit an image using Gemini API.
45
+
46
+ Args:
47
+ prompt: Text prompt for image generation
48
+ model: Gemini model to use
49
+ input_image: Optional input image for editing
50
+ aspect_ratio: Optional aspect ratio (e.g., "9:16", "16:9", "1:1", "3:4", "4:3")
51
+
52
+ Returns:
53
+ Tuple of (image_bytes, mime_type)
54
+ """
55
+ api_key = get_credential("GEMINI_API_KEY", "nano-banana")
56
+ url = f"{API_URL}/{model}:generateContent?key={api_key}"
57
+
58
+ parts: list[dict] = [{"text": prompt}]
59
+
60
+ if input_image:
61
+ if not input_image.exists():
62
+ print(f"Error: Input file not found: {input_image}")
63
+ sys.exit(1)
64
+
65
+ image_data = input_image.read_bytes()
66
+ base64_data = base64.b64encode(image_data).decode("utf-8")
67
+
68
+ suffix = input_image.suffix.lower()
69
+ mime_map = {
70
+ ".jpg": "image/jpeg",
71
+ ".jpeg": "image/jpeg",
72
+ ".png": "image/png",
73
+ ".webp": "image/webp",
74
+ ".gif": "image/gif",
75
+ }
76
+ mime_type = mime_map.get(suffix, "image/jpeg")
77
+
78
+ parts.append({"inlineData": {"mimeType": mime_type, "data": base64_data}})
79
+
80
+ generation_config: dict = {"responseModalities": ["TEXT", "IMAGE"]}
81
+
82
+ # Aspect ratio goes inside imageConfig (not directly in generationConfig)
83
+ if aspect_ratio:
84
+ generation_config["imageConfig"] = {"aspectRatio": aspect_ratio}
85
+
86
+ request_body = {
87
+ "contents": [{"parts": parts}],
88
+ "generationConfig": generation_config,
89
+ }
90
+
91
+ print(f"Generating image with {model}...")
92
+
93
+ with httpx.Client(timeout=TIMEOUT) as client:
94
+ response = client.post(
95
+ url,
96
+ json=request_body,
97
+ headers={"Content-Type": "application/json"},
98
+ )
99
+
100
+ if response.status_code != 200:
101
+ print(f"Error: API returned {response.status_code}")
102
+ print(response.text[:500])
103
+ sys.exit(1)
104
+
105
+ data = response.json()
106
+
107
+ candidates = data.get("candidates", [])
108
+ if not candidates:
109
+ print("Error: No candidates in response")
110
+ sys.exit(1)
111
+
112
+ parts = candidates[0].get("content", {}).get("parts", [])
113
+
114
+ for part in parts:
115
+ inline_data = part.get("inlineData")
116
+ if inline_data:
117
+ mime_type = inline_data.get("mimeType", "image/png")
118
+ base64_data = inline_data.get("data", "")
119
+ image_bytes = base64.b64decode(base64_data)
120
+ return image_bytes, mime_type
121
+
122
+ print("Error: No image data in response")
123
+ print("Response parts:", [list(p.keys()) for p in parts])
124
+ sys.exit(1)
125
+
126
+
127
+ def main():
128
+ parser = argparse.ArgumentParser(
129
+ description="Generate images using Gemini API",
130
+ formatter_class=argparse.RawDescriptionHelpFormatter,
131
+ epilog="""
132
+ Examples:
133
+ %(prog)s "A sunset over mountains"
134
+ %(prog)s "A cute robot" --output robot.png
135
+ %(prog)s "Add a hat" --input photo.jpg --output edited.png
136
+ %(prog)s "prompt" --model imagen-3.0-generate-002
137
+ """,
138
+ )
139
+ parser.add_argument("prompt", help="Image generation prompt")
140
+ parser.add_argument(
141
+ "--output", "-o",
142
+ help="Output file path (default: generated_<timestamp>.png)",
143
+ )
144
+ parser.add_argument(
145
+ "--input", "-i",
146
+ type=Path,
147
+ help="Input image for editing (optional)",
148
+ )
149
+ parser.add_argument(
150
+ "--model", "-m",
151
+ default=DEFAULT_MODEL,
152
+ help=f"Model to use (default: {DEFAULT_MODEL})",
153
+ )
154
+ parser.add_argument(
155
+ "--pro", "-p",
156
+ action="store_true",
157
+ help=f"Use pro model ({PRO_MODEL}) for higher quality",
158
+ )
159
+ parser.add_argument(
160
+ "--aspect-ratio", "-a",
161
+ help="Aspect ratio: 9:16 (portrait), 16:9 (landscape), 1:1 (square), 3:4, 4:3",
162
+ )
163
+
164
+ args = parser.parse_args()
165
+
166
+ # Override model if pro flag is set
167
+ if args.pro:
168
+ args.model = PRO_MODEL
169
+
170
+ image_bytes, mime_type = generate_image(
171
+ prompt=args.prompt,
172
+ model=args.model,
173
+ input_image=args.input,
174
+ aspect_ratio=args.aspect_ratio,
175
+ )
176
+
177
+ if args.output:
178
+ output_path = Path(args.output)
179
+ else:
180
+ # Default: save to _archive folder
181
+ ARCHIVE_DIR.mkdir(exist_ok=True)
182
+ ext = ".png" if "png" in mime_type else ".jpg"
183
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
184
+ output_path = ARCHIVE_DIR / f"generated_{timestamp}{ext}"
185
+
186
+ output_path.write_bytes(image_bytes)
187
+ print(f"Saved: {output_path} ({len(image_bytes):,} bytes)")
188
+
189
+
190
+ if __name__ == "__main__":
191
+ main()
@@ -0,0 +1,41 @@
1
+ ---
2
+ name: news
3
+ description: Search recent news articles via `cue news`.
4
+ category: data
5
+ type: context
6
+ metadata:
7
+ short-description: Search news articles
8
+ scope: first-party
9
+ supports-json-output: true
10
+ ---
11
+
12
+ Search news articles via backend NewsAPI integration.
13
+
14
+ ## Requirements
15
+
16
+ - `cue` CLI installed and authenticated (`cue` then `/auth`)
17
+ - Backend configured with `NEWSAPI_API_KEY` for results
18
+
19
+ ## Usage
20
+
21
+ ```bash
22
+ cue news search "latest AI model releases"
23
+ cue news search "OpenAI" --from 2026-03-01 --to 2026-03-04 --sort-by popularity --page-size 10
24
+ cue news search "NVIDIA earnings" --json
25
+ ```
26
+
27
+ ## Options
28
+
29
+ - `--from YYYY-MM-DD` start date
30
+ - `--to YYYY-MM-DD` end date
31
+ - `--sort-by relevancy|popularity|publishedAt`
32
+ - `--language <2-letter-code>` (default: `en`)
33
+ - `--page-size <1-100>` (default: `20`)
34
+ - `--page <1-100>` (default: `1`)
35
+ - `--json` machine-readable output
36
+ - `--compact` human-readable output (default)
37
+
38
+ ## Notes
39
+
40
+ - Endpoint: `POST /api/v1/news/search`
41
+ - Missing backend NewsAPI key returns `NEWS_NOT_CONFIGURED`.