makaron-cli 0.4.0 → 0.5.0

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 (4) hide show
  1. package/README.md +115 -204
  2. package/SKILL.md +197 -0
  3. package/bin/makaron.mjs +100 -13
  4. package/package.json +4 -2
package/README.md CHANGED
@@ -1,287 +1,198 @@
1
- # makaron-cli
1
+ # Makaron CLI — Agent Integration Skill
2
2
 
3
- Talk to **Makaron Agent** from the terminal create projects, edit images, generate videos, and compose music.
3
+ Makaron is a multimodal AI creative agent. You talk to it via `makaron chat`, and it produces images, videos, music, and animated designs — all saved to a persistent project.
4
4
 
5
- Zero dependencies. Single file. Works with `npx`.
6
-
7
- ## Install
5
+ ## Setup
8
6
 
9
7
  ```bash
10
- npx makaron-cli # run directly (always latest)
11
- # or
12
8
  npm install -g makaron-cli
13
- ```
14
-
15
- ## Authentication
16
-
17
- ### API Key (recommended for agents)
9
+ # or use directly: npx makaron-cli
18
10
 
19
- Set the `MAKARON_API_KEY` environment variable. That's it — no login step needed.
20
-
21
- ```bash
22
11
  export MAKARON_API_KEY=mk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
23
- npx makaron-cli list
24
12
  ```
25
13
 
26
- Get your API key from the Makaron dashboard (Settings → API Keys), or ask your admin to generate one.
14
+ Verify: `npx makaron-cli list` should show projects.
27
15
 
28
- ### Interactive login (humans)
16
+ ## Core Workflow
29
17
 
30
18
  ```bash
31
- npx makaron-cli login
32
- # Enter email + password token saved to ~/.makaron/auth.json
33
- ```
19
+ # 1. Create project with image(s)
20
+ PROJECT_ID=$(npx makaron-cli create --image photo.jpg 2>/dev/null | grep "ID:" | awk '{print $2}')
34
21
 
35
- The CLI checks `MAKARON_API_KEY` first, then falls back to the saved session in `~/.makaron/auth.json`.
22
+ # Multi-image project:
23
+ PROJECT_ID=$(npx makaron-cli create --image img1.jpg --image img2.jpg 2>/dev/null | grep "ID:" | awk '{print $2}')
36
24
 
37
- ## Commands
25
+ # 2. Submit creative request (non-blocking)
26
+ RUN_ID=$(npx makaron-cli chat --project $PROJECT_ID -b "make it cinematic and create a 5s video")
38
27
 
39
- ### `chat` Send a message to Makaron Agent
40
-
41
- This is the main command. The Agent can edit images, generate videos, compose music, and create designs.
42
-
43
- **Default mode (non-blocking poll):**
44
-
45
- ```bash
46
- npx makaron-cli chat --project <id> "make it look cinematic"
28
+ # 3. Watch until all artifacts are ready
29
+ npx makaron-cli responses watch $RUN_ID --jsonl
47
30
  ```
48
31
 
49
- The CLI submits the request and polls every 3s for results. No long-lived connection — you can Ctrl+C and come back later with `responses get`.
32
+ ## Primary: `chat` (Agent-driven creative work)
50
33
 
51
- **Background modesubmit and exit immediately:**
34
+ Use `chat` for all creative tasks. Makaron Agent decides how to execute it can edit images, generate videos, compose music, and create designs in a single conversation.
35
+
36
+ ### Submit a request
52
37
 
53
38
  ```bash
54
- npx makaron-cli chat --project <id> --background "make a 5s video"
55
- # → prints runId and exits in <1s
39
+ npx makaron-cli chat --project <id> --json -b "<prompt>"
40
+ ```
56
41
 
57
- # Later, check the result:
58
- npx makaron-cli responses get <runId> --wait
42
+ Returns immediately:
43
+ ```json
44
+ {"runId": "xxx", "projectId": "...", "projectUrl": "https://www.makaron.app/projects/...", "status": "running"}
59
45
  ```
60
46
 
61
- **Structured JSON output (for programmatic use):**
47
+ ### With additional images
62
48
 
63
49
  ```bash
64
- npx makaron-cli chat --project <id> --json --background "edit the photo"
65
- # {"runId":"...","projectId":"...","projectUrl":"...","status":"running"}
50
+ # Add reference images to project before chatting
51
+ npx makaron-cli chat --project <id> --image ref1.jpg --image ref2.jpg -b "use these as style reference"
66
52
  ```
67
53
 
68
- **Legacy streaming mode (real-time SSE):**
54
+ ### Check status (single query)
69
55
 
70
56
  ```bash
71
- npx makaron-cli chat --project <id> --stream "hello"
57
+ npx makaron-cli responses get <runId> --json
72
58
  ```
73
59
 
74
- **Options:**
75
- - `--project <id>` — target project (required)
76
- - `--image <file>` — upload image to project before chatting
77
- - `--background` / `-b` — submit and exit, print runId
78
- - `--json` — structured JSON output
79
- - `--stream` — legacy real-time SSE mode
80
- - `--video-model kling|seedance` — preferred video model
81
- - `--model <name>` — preferred image model
82
-
83
- ### `responses` — Query run status and results
60
+ ### Watch until done (streaming events)
84
61
 
85
62
  ```bash
86
- # Get current status (single query)
87
- npx makaron-cli responses get <runId>
88
-
89
- # Poll until completed
90
- npx makaron-cli responses get <runId> --wait
91
-
92
- # Wait for video/music rendering too (not just Agent completion)
93
- npx makaron-cli responses get <runId> --wait --wait-artifacts
94
-
95
- # List runs for a project
96
- npx makaron-cli responses list --project <id>
63
+ npx makaron-cli responses watch <runId> --jsonl
97
64
  ```
98
65
 
99
- **Result JSON structure:**
100
-
101
- ```json
102
- {
103
- "runId": "...",
104
- "projectId": "...",
105
- "status": "completed",
106
- "eventCount": 31,
107
- "result": {
108
- "text": "Agent's text response...",
109
- "images": [{ "snapshotId": "...", "imageUrl": "https://..." }],
110
- "designs": [{
111
- "snapshotId": "...",
112
- "imageUrl": "https://...",
113
- "width": 1080,
114
- "height": 1920,
115
- "animation": { "fps": 30, "durationInSeconds": 3 },
116
- "props": { "title": "..." },
117
- "code": "function Design(props) { ... }"
118
- }],
119
- "videos": [{ "taskId": "...", "status": "completed", "videoUrl": "https://..." }],
120
- "music": [{ "taskId": "...", "status": "completed", "audioUrl": "https://..." }]
121
- }
122
- }
66
+ Outputs one JSON per line as artifacts appear:
123
67
  ```
124
-
125
- ### `list` — Show all projects
126
-
127
- ```bash
128
- npx makaron-cli list
68
+ {"event":"output.added","item":{"id":"out_1","type":"image","status":"completed","url":"https://..."}}
69
+ {"event":"output.added","item":{"id":"out_2","type":"video","status":"rendering","task_id":"xxx"}}
70
+ {"event":"output.updated","item":{"id":"out_2","type":"video","status":"completed","url":"https://..."}}
71
+ {"event":"done","status":"completed"}
129
72
  ```
130
73
 
131
- ### `create` Create a new project
74
+ ### Extract specific results
132
75
 
133
76
  ```bash
134
- # From local image file(s)
135
- npx makaron-cli create --image photo.jpg
136
- npx makaron-cli create --image img1.jpg --image img2.jpg
137
-
138
- # From URL(s)
139
- npx makaron-cli create --image-url https://example.com/photo.jpg
140
-
141
- # Empty project (for text-to-image)
142
- npx makaron-cli create --title "My New Project"
77
+ npx makaron-cli responses get <runId> --pick first_image_url
78
+ npx makaron-cli responses get <runId> --pick image_urls # all images (JSON array)
79
+ npx makaron-cli responses get <runId> --pick first_video_url
80
+ npx makaron-cli responses get <runId> --pick video_urls # all videos
81
+ npx makaron-cli responses get <runId> --pick project_url
82
+ npx makaron-cli responses get <runId> --pick text # agent's text reply
83
+ npx makaron-cli responses get <runId> --pick output # full output array
84
+ npx makaron-cli responses get <runId> --pick status
143
85
  ```
144
86
 
145
- ### `abort` Stop a running Agent
87
+ ## Fallback: Direct tool calls (no project context)
146
88
 
147
- ```bash
148
- npx makaron-cli abort <runId>
149
- ```
150
-
151
- ### `edit` — AI image editing (direct MCP tool call)
89
+ Use these only when `chat` is unavailable or you need raw model access without project/conversation context.
152
90
 
153
- Unlike `chat` (which uses the Agent with project context), `edit` directly calls the image generation model for one-shot results.
91
+ ### `edit` One-shot image editing
154
92
 
155
93
  ```bash
156
- # Text-to-image (no input image)
157
- npx makaron-cli edit "a cyberpunk cityscape at night, neon reflections"
158
-
159
94
  # Edit an existing image
160
95
  npx makaron-cli edit --image photo.jpg "add cinematic warm lighting"
161
96
 
162
- # With model and skill
163
- npx makaron-cli edit --image photo.jpg --model openai --skill captions "add elegant title"
97
+ # Text-to-image (no input)
98
+ npx makaron-cli edit "a cyberpunk cityscape at night"
164
99
 
165
- # With reference images (up to 3)
166
- npx makaron-cli edit --image photo.jpg --ref style1.jpg --ref style2.jpg "match this style"
100
+ # With model/skill/reference
101
+ npx makaron-cli edit --image photo.jpg --model openai --skill captions "add title"
102
+ npx makaron-cli edit --image photo.jpg --ref style.jpg "match this style"
167
103
 
168
- # Specify output path and aspect ratio
169
- npx makaron-cli edit --out result.jpg --aspect 9:16 "vertical poster design"
104
+ # Output to file
105
+ npx makaron-cli edit --image photo.jpg --out result.jpg "make it dramatic"
170
106
  ```
171
107
 
172
- Options:
173
- - `--image <file|url>` — input image (omit for text-to-image)
174
- - `--model gemini|qwen|openai|pony|wai` — model selection (default: auto)
175
- - `--skill enhance|creative|wild|captions` — activate skill template
176
- - `--ref <file|url>` — reference image (repeatable, up to 3)
177
- - `--aspect <ratio>` — target aspect ratio (e.g. `4:5`, `1:1`, `16:9`)
178
- - `--out <path>` — output file path (default: `makaron-output-{timestamp}.jpg`)
108
+ Options: `--image`, `--model gemini|qwen|openai|pony|wai`, `--skill enhance|creative|wild|captions`, `--ref <file>` (up to 3), `--aspect <ratio>`, `--out <path>`
179
109
 
180
- ### `video` — Video generation
110
+ ### `video` — Video generation (3 steps)
181
111
 
182
112
  ```bash
183
- # Write a video script from images
184
- npx makaron-cli video script --image img1.jpg --image img2.jpg "cinematic story"
113
+ # 1. Write script from images
114
+ npx makaron-cli video script --image img1.jpg "cinematic story"
185
115
 
186
- # Submit video rendering (images must be public URLs)
187
- npx makaron-cli video create --script "Shot 1..." --image https://...img1.jpg --duration 10
116
+ # 2. Submit rendering (images must be public URLs from step 1 or uploaded)
117
+ npx makaron-cli video create --script "Shot 1 (5s): <<<image_1>>> ..." --image https://...jpg --duration 5 --model kling
188
118
 
189
- # Check status
119
+ # 3. Check status
190
120
  npx makaron-cli video status <taskId>
191
121
  ```
192
122
 
123
+ Options for `video create`: `--script "..."`, `--script-file <path>`, `--image <url>` (repeatable, up to 7), `--duration 3|5|7|10|15`, `--aspect 9:16|16:9|1:1`, `--model kling|seedance`
124
+
193
125
  ### `music` — Music generation
194
126
 
195
127
  ```bash
196
- # Generate instrumental music
197
128
  npx makaron-cli music create "gentle piano, warm strings, cinematic"
198
-
199
- # With vocals and style
200
- npx makaron-cli music create --vocals --style "lo-fi, ambient" "rainy day vibes"
201
-
202
- # Check status
129
+ npx makaron-cli music create --vocals --style "lo-fi" "rainy day vibes"
203
130
  npx makaron-cli music status <taskId>
204
131
  ```
205
132
 
206
- ## Agent Integration Guide
207
-
208
- For AI agents (Claude Code, OpenClaw, etc.) calling this CLI programmatically:
209
-
210
- ### Non-blocking workflow (recommended)
211
-
212
- ```bash
213
- # 1. Submit task — returns immediately
214
- RUN_ID=$(npx makaron-cli chat --project $PROJECT_ID --background "make a 5s video")
215
-
216
- # 2. Do other work while Makaron processes...
217
-
218
- # 3. Poll for result when ready
219
- npx makaron-cli responses get $RUN_ID --wait
220
- ```
133
+ Options: `--vocals` (include vocals), `--style "genre"`
221
134
 
222
- ### JSON mode for structured parsing
135
+ ## Response Schema
223
136
 
224
- ```bash
225
- # Submit
226
- RESULT=$(npx makaron-cli chat --project $ID --json --background "edit the photo")
227
- RUN_ID=$(echo $RESULT | jq -r .runId)
137
+ ```typescript
138
+ type MakaronRunResponse = {
139
+ id: string
140
+ status: "in_progress" | "completed" | "failed" | "aborted"
141
+ incomplete: boolean // true = keep polling
142
+ project_id: string
143
+ project_url: string
144
+ next_poll_after_ms?: number // suggested poll interval
145
+ output: MakaronOutput[]
146
+ }
228
147
 
229
- # Poll
230
- npx makaron-cli responses get $RUN_ID --wait
148
+ type MakaronOutput =
149
+ | { id: string; type: "text"; status: "completed"; content: string }
150
+ | { id: string; type: "image"; status: "completed"; url: string; snapshot_id: string }
151
+ | { id: string; type: "design"; status: "completed"; url: string; width: number; height: number; animated: boolean; duration?: number }
152
+ | { id: string; type: "video"; status: "queued"|"rendering"|"completed"|"failed"; task_id: string; url?: string; elapsed_seconds?: number }
153
+ | { id: string; type: "music"; status: "queued"|"rendering"|"completed"|"failed"; task_id: string; url?: string; elapsed_seconds?: number }
231
154
  ```
232
155
 
233
- ### Sequential multi-turn
156
+ ## Polling Rules
234
157
 
235
- ```bash
236
- # Turn 1: edit image
237
- npx makaron-cli chat --project $ID "make it cinematic"
238
- # Wait for completion (default mode polls automatically)
158
+ 1. Poll while `incomplete: true` or `status` is `"in_progress"`
159
+ 2. Use `next_poll_after_ms` as interval (default 5000ms)
160
+ 3. Stop when `status` is `"completed"`, `"failed"`, or `"aborted"`
161
+ 4. Top-level `status: "completed"` means ALL artifacts are ready (including rendered videos)
239
162
 
240
- # Turn 2: make video from the edited image
241
- npx makaron-cli chat --project $ID "now make a 5s video from this"
242
- ```
163
+ ## Exit Codes
243
164
 
244
- Note: One project runs one Agent at a time. A new message while the previous is still running will interrupt the first.
165
+ | Code | Meaning |
166
+ |------|---------|
167
+ | 0 | Success (completed) or valid in-progress response |
168
+ | 1 | Failed, aborted, or HTTP error |
169
+ | 2 | Timeout (partial response still printed to stdout) |
245
170
 
246
- ### What the Agent can do
171
+ ## What Makaron Agent Can Do
247
172
 
248
- | Capability | Example prompt |
249
- |-----------|---------------|
250
- | Image editing | "make the sky more dramatic" |
173
+ | Task | Example prompt |
174
+ |------|---------------|
175
+ | Edit photo | "make it cinematic with warm tones" |
251
176
  | Style transfer | "convert to oil painting style" |
252
- | Add elements | "add a cat sitting on the table" |
253
- | Remove elements | "remove the person in the background" |
254
- | Text-to-image | "generate a cyberpunk cityscape" (on empty project) |
255
- | Video from image | "create a 5 second video" |
256
- | Video with script | "make a video: camera slowly zooms in while leaves fall" |
177
+ | Add/remove elements | "add a cat on the table" / "remove background person" |
178
+ | Text-to-image | "generate a cyberpunk cityscape" |
179
+ | Video from image | "create a 5 second video of her walking" |
180
+ | Video with model | "用seedance模型做5秒视频" |
257
181
  | Background music | "add calm piano music" |
258
- | Design/motion graphics | "create an Instagram story with animated text" |
259
-
260
- ### Output types in result
261
-
262
- | Type | Field | Contains |
263
- |------|-------|----------|
264
- | Text | `result.text` | Agent's conversational response |
265
- | Image | `result.images[].imageUrl` | Generated/edited image URL |
266
- | Design (still) | `result.designs[].imageUrl` | Poster screenshot + code |
267
- | Design (animated) | `result.designs[].animation` | fps + duration + code |
268
- | Video | `result.videos[].videoUrl` | MP4 URL (after rendering) |
269
- | Music | `result.music[].audioUrl` | MP3 URL (after generating) |
270
-
271
- ### Environment variables
272
-
273
- | Variable | Purpose | Default |
274
- |----------|---------|---------|
275
- | `MAKARON_API_KEY` | API key (`mk_live_xxx`) | — |
276
- | `MAKARON_URL` | API endpoint override | `https://www.makaron.app` |
277
-
278
- ## Viewing results
279
-
280
- All generated content (images, videos, designs) is saved to the project and visible at:
281
-
282
- ```
283
- https://www.makaron.app/projects/<project-id>
284
- ```
182
+ | Motion design | "create an Instagram story with animated text" |
183
+ | Multi-step | "edit the photo then make a video from it" |
184
+
185
+ ## Important Notes
186
+
187
+ - One project = one conversation thread. All history is preserved.
188
+ - One run at a time per project. New message interrupts previous run.
189
+ - Multi-image: `create --image a.jpg --image b.jpg` or `chat --image ref.jpg`.
190
+ - Videos take 2-5 minutes to render. Use `watch` to get URL when ready.
191
+ - Music takes ~60 seconds. Appears in output when done.
192
+ - Images are typically ready in 15-30 seconds.
193
+ - stdout is always machine-readable JSON/text. Human-friendly logs go to stderr.
194
+ - Always use `chat` as the primary interface — even for single image edits.
195
+ - `edit`/`video`/`music` are fallback tools for when `chat` is unavailable or you need raw model access without project context.
285
196
 
286
197
  ## Admin: Skill Marketplace Operations
287
198
 
package/SKILL.md ADDED
@@ -0,0 +1,197 @@
1
+ ---
2
+ name: makaron
3
+ description: Use Makaron CLI to generate AI images, videos, music, and motion designs. Trigger when user needs creative media production — photo editing, video generation, music composition, or design creation. Requires `npx makaron-cli` and MAKARON_API_KEY env var.
4
+ ---
5
+
6
+ # Makaron CLI — Agent Integration Skill
7
+
8
+ Makaron is a multimodal AI creative agent. You talk to it via `makaron chat`, and it produces images, videos, music, and animated designs — all saved to a persistent project.
9
+
10
+ ## Setup
11
+
12
+ ```bash
13
+ export MAKARON_API_KEY=mk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
14
+ ```
15
+
16
+ Verify: `npx makaron-cli list` should show projects.
17
+
18
+ ## Core Workflow
19
+
20
+ ```bash
21
+ # 1. Create project with image(s)
22
+ PROJECT_ID=$(npx makaron-cli create --image photo.jpg 2>/dev/null | grep "ID:" | awk '{print $2}')
23
+
24
+ # Multi-image project:
25
+ PROJECT_ID=$(npx makaron-cli create --image img1.jpg --image img2.jpg 2>/dev/null | grep "ID:" | awk '{print $2}')
26
+
27
+ # 2. Submit creative request (non-blocking)
28
+ RUN_ID=$(npx makaron-cli chat --project $PROJECT_ID -b "make it cinematic and create a 5s video")
29
+
30
+ # 3. Watch until all artifacts are ready
31
+ npx makaron-cli responses watch $RUN_ID --jsonl
32
+ ```
33
+
34
+ ## Primary: `chat` (Agent-driven creative work)
35
+
36
+ Use `chat` for all creative tasks. Makaron Agent decides how to execute — it can edit images, generate videos, compose music, and create designs in a single conversation.
37
+
38
+ ### Submit a request
39
+
40
+ ```bash
41
+ npx makaron-cli chat --project <id> --json -b "<prompt>"
42
+ ```
43
+
44
+ Returns immediately:
45
+ ```json
46
+ {"runId": "xxx", "projectId": "...", "projectUrl": "https://www.makaron.app/projects/...", "status": "running"}
47
+ ```
48
+
49
+ ### With additional images
50
+
51
+ ```bash
52
+ # Add reference images to project before chatting
53
+ npx makaron-cli chat --project <id> --image ref1.jpg --image ref2.jpg -b "use these as style reference"
54
+ ```
55
+
56
+ ### Check status (single query)
57
+
58
+ ```bash
59
+ npx makaron-cli responses get <runId> --json
60
+ ```
61
+
62
+ ### Watch until done (streaming events)
63
+
64
+ ```bash
65
+ npx makaron-cli responses watch <runId> --jsonl
66
+ ```
67
+
68
+ Outputs one JSON per line as artifacts appear:
69
+ ```
70
+ {"event":"output.added","item":{"id":"out_1","type":"image","status":"completed","url":"https://..."}}
71
+ {"event":"output.added","item":{"id":"out_2","type":"video","status":"rendering","task_id":"xxx"}}
72
+ {"event":"output.updated","item":{"id":"out_2","type":"video","status":"completed","url":"https://..."}}
73
+ {"event":"done","status":"completed"}
74
+ ```
75
+
76
+ ### Extract specific results
77
+
78
+ ```bash
79
+ npx makaron-cli responses get <runId> --pick first_image_url
80
+ npx makaron-cli responses get <runId> --pick image_urls # all images (JSON array)
81
+ npx makaron-cli responses get <runId> --pick first_video_url
82
+ npx makaron-cli responses get <runId> --pick video_urls # all videos
83
+ npx makaron-cli responses get <runId> --pick project_url
84
+ npx makaron-cli responses get <runId> --pick text # agent's text reply
85
+ npx makaron-cli responses get <runId> --pick output # full output array
86
+ npx makaron-cli responses get <runId> --pick status
87
+ ```
88
+
89
+ ## Fallback: Direct tool calls (no project context)
90
+
91
+ Use these only when `chat` is unavailable or you need raw model access without project/conversation context.
92
+
93
+ ### `edit` — One-shot image editing
94
+
95
+ ```bash
96
+ # Edit an existing image
97
+ npx makaron-cli edit --image photo.jpg "add cinematic warm lighting"
98
+
99
+ # Text-to-image (no input)
100
+ npx makaron-cli edit "a cyberpunk cityscape at night"
101
+
102
+ # With model/skill/reference
103
+ npx makaron-cli edit --image photo.jpg --model openai --skill captions "add title"
104
+ npx makaron-cli edit --image photo.jpg --ref style.jpg "match this style"
105
+
106
+ # Output to file
107
+ npx makaron-cli edit --image photo.jpg --out result.jpg "make it dramatic"
108
+ ```
109
+
110
+ Options: `--image`, `--model gemini|qwen|openai|pony|wai`, `--skill enhance|creative|wild|captions`, `--ref <file>` (up to 3), `--aspect <ratio>`, `--out <path>`
111
+
112
+ ### `video` — Video generation (3 steps)
113
+
114
+ ```bash
115
+ # 1. Write script from images
116
+ npx makaron-cli video script --image img1.jpg "cinematic story"
117
+
118
+ # 2. Submit rendering (images must be public URLs from step 1 or uploaded)
119
+ npx makaron-cli video create --script "Shot 1 (5s): <<<image_1>>> ..." --image https://...jpg --duration 5 --model kling
120
+
121
+ # 3. Check status
122
+ npx makaron-cli video status <taskId>
123
+ ```
124
+
125
+ Options for `video create`: `--script "..."`, `--script-file <path>`, `--image <url>` (repeatable, up to 7), `--duration 3|5|7|10|15`, `--aspect 9:16|16:9|1:1`, `--model kling|seedance`
126
+
127
+ ### `music` — Music generation
128
+
129
+ ```bash
130
+ npx makaron-cli music create "gentle piano, warm strings, cinematic"
131
+ npx makaron-cli music create --vocals --style "lo-fi" "rainy day vibes"
132
+ npx makaron-cli music status <taskId>
133
+ ```
134
+
135
+ Options: `--vocals` (include vocals), `--style "genre"`
136
+
137
+ ## Response Schema
138
+
139
+ ```typescript
140
+ type MakaronRunResponse = {
141
+ id: string
142
+ status: "in_progress" | "completed" | "failed" | "aborted"
143
+ incomplete: boolean // true = keep polling
144
+ project_id: string
145
+ project_url: string
146
+ next_poll_after_ms?: number // suggested poll interval
147
+ output: MakaronOutput[]
148
+ }
149
+
150
+ type MakaronOutput =
151
+ | { id: string; type: "text"; status: "completed"; content: string }
152
+ | { id: string; type: "image"; status: "completed"; url: string; snapshot_id: string }
153
+ | { id: string; type: "design"; status: "completed"; url: string; width: number; height: number; animated: boolean; duration?: number }
154
+ | { id: string; type: "video"; status: "queued"|"rendering"|"completed"|"failed"; task_id: string; url?: string; elapsed_seconds?: number }
155
+ | { id: string; type: "music"; status: "queued"|"rendering"|"completed"|"failed"; task_id: string; url?: string; elapsed_seconds?: number }
156
+ ```
157
+
158
+ ## Polling Rules
159
+
160
+ 1. Poll while `incomplete: true` or `status` is `"in_progress"`
161
+ 2. Use `next_poll_after_ms` as interval (default 5000ms)
162
+ 3. Stop when `status` is `"completed"`, `"failed"`, or `"aborted"`
163
+ 4. Top-level `status: "completed"` means ALL artifacts are ready (including rendered videos)
164
+
165
+ ## Exit Codes
166
+
167
+ | Code | Meaning |
168
+ |------|---------|
169
+ | 0 | Success (completed) or valid in-progress response |
170
+ | 1 | Failed, aborted, or HTTP error |
171
+ | 2 | Timeout (partial response still printed to stdout) |
172
+
173
+ ## What Makaron Agent Can Do
174
+
175
+ | Task | Example prompt |
176
+ |------|---------------|
177
+ | Edit photo | "make it cinematic with warm tones" |
178
+ | Style transfer | "convert to oil painting style" |
179
+ | Add/remove elements | "add a cat on the table" / "remove background person" |
180
+ | Text-to-image | "generate a cyberpunk cityscape" |
181
+ | Video from image | "create a 5 second video of her walking" |
182
+ | Video with model | "用seedance模型做5秒视频" |
183
+ | Background music | "add calm piano music" |
184
+ | Motion design | "create an Instagram story with animated text" |
185
+ | Multi-step | "edit the photo then make a video from it" |
186
+
187
+ ## Important Notes
188
+
189
+ - One project = one conversation thread. All history is preserved.
190
+ - One run at a time per project. New message interrupts previous run.
191
+ - Multi-image: `create --image a.jpg --image b.jpg` or `chat --image ref.jpg`.
192
+ - Videos take 2-5 minutes to render. Use `watch` to get URL when ready.
193
+ - Music takes ~60 seconds. Appears in output when done.
194
+ - Images are typically ready in 15-30 seconds.
195
+ - stdout is always machine-readable JSON/text. Human-friendly logs go to stderr.
196
+ - Always use `chat` as the primary interface — even for single image edits.
197
+ - `edit`/`video`/`music` are fallback tools for when `chat` is unavailable or you need raw model access without project context.
package/bin/makaron.mjs CHANGED
@@ -360,6 +360,79 @@ async function pollRun(baseUrl, headers, runId, opts = {}) {
360
360
  }
361
361
  }
362
362
 
363
+ // ─── Pick Helper ────────────────────────────────────────────────────────────
364
+
365
+ function applyPick(data, field) {
366
+ switch (field) {
367
+ case 'first_image_url': return data.output?.find(o => o.type === 'image')?.url || null;
368
+ case 'image_urls': return (data.output || []).filter(o => o.type === 'image' && o.url).map(o => o.url);
369
+ case 'first_video_url': return data.output?.find(o => o.type === 'video' && o.url)?.url || null;
370
+ case 'video_urls': return (data.output || []).filter(o => o.type === 'video' && o.url).map(o => o.url);
371
+ case 'first_design_url': return data.output?.find(o => o.type === 'design')?.url || null;
372
+ case 'design_urls': return (data.output || []).filter(o => o.type === 'design' && o.url).map(o => o.url);
373
+ case 'first_music_url': return data.output?.find(o => o.type === 'music' && o.url)?.url || null;
374
+ case 'music_urls': return (data.output || []).filter(o => o.type === 'music' && o.url).map(o => o.url);
375
+ case 'project_url': return data.project_url || null;
376
+ case 'output': return data.output || [];
377
+ case 'text': return data.output?.find(o => o.type === 'text')?.content || null;
378
+ case 'status': return data.status;
379
+ default: return data[field] !== undefined ? data[field] : null;
380
+ }
381
+ }
382
+
383
+ // ─── Watch (incremental event stream) ───────────────────────────────────────
384
+
385
+ async function watchRun(baseUrl, headers, runId, opts = {}) {
386
+ const { interval = 5000, jsonl = true } = opts;
387
+ let lastOutput = [];
388
+ const start = Date.now();
389
+
390
+ while (true) {
391
+ const elapsed = Math.round((Date.now() - start) / 1000);
392
+ let data;
393
+ try {
394
+ const res = await fetch(`${baseUrl}/api/agent/run/${runId}`, { headers });
395
+ if (!res.ok) {
396
+ if (elapsed > 800) { process.stderr.write(`Timeout after ${elapsed}s\n`); process.exit(2); }
397
+ await new Promise(r => setTimeout(r, interval));
398
+ continue;
399
+ }
400
+ data = await res.json();
401
+ } catch {
402
+ await new Promise(r => setTimeout(r, interval));
403
+ continue;
404
+ }
405
+
406
+ const currentOutput = data.output || [];
407
+
408
+ // Diff: find new or updated items
409
+ for (const item of currentOutput) {
410
+ const prev = lastOutput.find(o => o.id === item.id);
411
+ if (!prev) {
412
+ // New item
413
+ if (jsonl) console.log(JSON.stringify({ event: 'output.added', item }));
414
+ else process.stderr.write(`+ [${item.type}] ${item.url || item.content?.slice(0, 60) || item.status}\n`);
415
+ } else if (JSON.stringify(prev) !== JSON.stringify(item)) {
416
+ // Updated item (e.g. video status changed)
417
+ if (jsonl) console.log(JSON.stringify({ event: 'output.updated', item }));
418
+ else process.stderr.write(`~ [${item.type}] ${item.status} ${item.url || ''}\n`);
419
+ }
420
+ }
421
+
422
+ lastOutput = currentOutput;
423
+
424
+ // Check terminal status
425
+ if (!data.incomplete && (data.status === 'completed' || data.status === 'failed' || data.status === 'aborted')) {
426
+ if (jsonl) console.log(JSON.stringify({ event: 'done', status: data.status }));
427
+ if (data.status === 'failed' || data.status === 'aborted') process.exit(1);
428
+ process.exit(0);
429
+ }
430
+
431
+ const waitMs = data.next_poll_after_ms || interval;
432
+ await new Promise(r => setTimeout(r, waitMs));
433
+ }
434
+ }
435
+
363
436
  // ─── Async Task Polling ──────────────────────────────────────────────────────
364
437
 
365
438
  async function pollVideo(baseUrl, headers, taskId) {
@@ -605,34 +678,47 @@ if (command === 'login') {
605
678
 
606
679
  if (sub === 'get') {
607
680
  const runId = args[2];
608
- if (!runId) { console.error('Usage: makaron responses get <runId> [--wait] [--json]'); process.exit(1); }
609
- let wait = false, jsonOutput = false, waitForArtifacts = false;
681
+ if (!runId) { console.error('Usage: makaron responses get <runId> [--wait] [--json] [--pick <field>]'); process.exit(1); }
682
+ let wait = false, jsonOutput = false, pick = null;
610
683
  for (let i = 3; i < args.length; i++) {
611
684
  if (args[i] === '--wait') wait = true;
612
685
  if (args[i] === '--json') jsonOutput = true;
613
- if (args[i] === '--wait-artifacts') waitForArtifacts = true;
686
+ if (args[i] === '--pick' && args[i + 1]) pick = args[++i];
614
687
  }
615
688
 
616
689
  if (wait) {
617
- await pollRun(baseUrl, headers, runId, { json: jsonOutput, waitForArtifacts });
690
+ await pollRun(baseUrl, headers, runId, { json: true });
618
691
  } else {
619
- const params = new URLSearchParams();
620
- if (waitForArtifacts) params.set('wait_for_artifacts', 'true');
621
- const res = await fetch(`${baseUrl}/api/agent/run/${runId}?${params}`, { headers });
622
- if (!res.ok) { console.error(`Error ${res.status}:`, await res.text()); process.exit(1); }
692
+ const res = await fetch(`${baseUrl}/api/agent/run/${runId}`, { headers });
693
+ if (!res.ok) { process.stderr.write(`Error ${res.status}: ${await res.text()}\n`); process.exit(1); }
623
694
  const data = await res.json();
624
- console.log(JSON.stringify(data, null, 2));
695
+ if (pick) {
696
+ const picked = applyPick(data, pick);
697
+ if (picked !== undefined) console.log(typeof picked === 'string' ? picked : JSON.stringify(picked));
698
+ } else {
699
+ console.log(JSON.stringify(data, null, 2));
700
+ }
701
+ if (data.status === 'failed' || data.status === 'aborted') process.exit(1);
625
702
  }
626
703
 
704
+ } else if (sub === 'watch') {
705
+ const runId = args[2];
706
+ if (!runId) { console.error('Usage: makaron responses watch <runId> [--jsonl] [--interval <ms>]'); process.exit(1); }
707
+ let interval = 5000, jsonl = false;
708
+ for (let i = 3; i < args.length; i++) {
709
+ if (args[i] === '--jsonl') jsonl = true;
710
+ if (args[i] === '--interval' && args[i + 1]) interval = parseInt(args[++i]);
711
+ }
712
+ await watchRun(baseUrl, headers, runId, { interval, jsonl });
713
+
627
714
  } else if (sub === 'list') {
628
715
  let projectId = null;
629
716
  for (let i = 2; i < args.length; i++) {
630
717
  if (args[i] === '--project' && args[i + 1]) projectId = args[++i];
631
718
  }
632
719
  if (!projectId) { console.error('Usage: makaron responses list --project <id>'); process.exit(1); }
633
- // Query runs for project
634
720
  const res = await fetch(`${baseUrl}/api/agent/run?projectId=${projectId}`, { headers });
635
- if (!res.ok) { console.error(`Error ${res.status}:`, await res.text()); process.exit(1); }
721
+ if (!res.ok) { process.stderr.write(`Error ${res.status}: ${await res.text()}\n`); process.exit(1); }
636
722
  const data = await res.json();
637
723
  if (data.runs?.length) {
638
724
  for (const r of data.runs) {
@@ -645,9 +731,10 @@ if (command === 'login') {
645
731
 
646
732
  } else {
647
733
  console.log(`Responses commands:
648
- responses get <runId> Get run status and results
734
+ responses get <runId> Get status and output (JSON)
649
735
  responses get <runId> --wait Poll until completed
650
- responses get <runId> --wait-artifacts Wait for video/music too
736
+ responses get <runId> --pick <field> Extract: first_image_url, first_video_url, project_url, output
737
+ responses watch <runId> --jsonl Watch until done (incremental events)
651
738
  responses list --project <id> List runs for a project
652
739
  `);
653
740
  }
package/package.json CHANGED
@@ -1,13 +1,15 @@
1
1
  {
2
2
  "name": "makaron-cli",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Talk to Makaron Agent from the terminal — create projects, edit images, generate videos",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "makaron": "./bin/makaron.mjs"
8
8
  },
9
9
  "files": [
10
- "bin/"
10
+ "bin/",
11
+ "SKILL.md",
12
+ "README.md"
11
13
  ],
12
14
  "keywords": [
13
15
  "makaron",