@plusonelabs/cue 0.0.93 → 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.
- package/bin/cue.js +10 -10
- package/bin/windows-bootstrap.ps1 +9 -9
- package/bin/windows-runtime-artifact.json +2 -2
- package/dist/cli.mjs +1088 -821
- package/dist/skills/README.md +199 -0
- package/dist/skills/_lib/credentials.py +72 -0
- package/dist/skills/activity/SKILL.md +97 -0
- package/dist/skills/assistant/SKILL.md +173 -0
- package/dist/skills/audio/SKILL.md +132 -0
- package/dist/skills/elevenlabs-tts/SKILL.md +76 -0
- package/dist/skills/elevenlabs-tts/scripts/speak.ts +226 -0
- package/dist/skills/event/SKILL.md +98 -0
- package/dist/skills/gemini-search/SKILL.md +52 -0
- package/dist/skills/gemini-search/generate.py +195 -0
- package/dist/skills/image/SKILL.md +169 -0
- package/dist/skills/like/SKILL.md +66 -0
- package/dist/skills/listen/SKILL.md +57 -0
- package/dist/skills/listen/scripts/listen.sh +74 -0
- package/dist/skills/listen/scripts/record.swift +94 -0
- package/dist/skills/markdown-to-pdf/SKILL.md +31 -0
- package/dist/skills/message/SKILL.md +136 -0
- package/dist/skills/mini-apps/SKILL.md +256 -0
- package/dist/skills/music/SKILL.md +139 -0
- package/dist/skills/nano-banana/SKILL.md +70 -0
- package/dist/skills/nano-banana/generate.py +191 -0
- package/dist/skills/news/SKILL.md +41 -0
- package/dist/skills/notify/SKILL.md +123 -0
- package/dist/skills/places/SKILL.md +215 -0
- package/dist/skills/posts/SKILL.md +440 -0
- package/dist/skills/project/SKILL.md +116 -0
- package/dist/skills/pulse/SKILL.md +106 -0
- package/dist/skills/reddit/SKILL.md +41 -0
- package/dist/skills/seeddance/SKILL.md +81 -0
- package/dist/skills/seeddance/generate.py +303 -0
- package/dist/skills/seedream/SKILL.md +86 -0
- package/dist/skills/seedream/generate.py +301 -0
- package/dist/skills/social-graph/SKILL.md +119 -0
- package/dist/skills/transcribe/SKILL.md +150 -0
- package/dist/skills/transcribe/generate.py +389 -0
- package/dist/skills/user/SKILL.md +180 -0
- package/dist/skills/veo3/SKILL.md +76 -0
- package/dist/skills/veo3/generate.py +339 -0
- package/dist/skills/video/SKILL.md +163 -0
- package/dist/skills/weather/SKILL.md +101 -0
- package/dist/skills/web-fetch/SKILL.md +43 -0
- package/dist/skills/web-search/SKILL.md +52 -0
- package/dist/skills/z-asr/SKILL.md +58 -0
- package/dist/skills/z-asr/generate.py +177 -0
- package/dist/skills/z-search/SKILL.md +57 -0
- package/dist/skills/z-search/generate.py +189 -0
- package/dist/skills/z-tts/SKILL.md +51 -0
- package/dist/skills/z-tts/generate.py +172 -0
- 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`.
|