makaron-cli 0.8.4 → 0.10.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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "makaron-cli",
3
- "version": "0.8.4",
3
+ "version": "0.9.0",
4
4
  "description": "AI image editing, video generation, and music creation via CLI. Agents can self-register, create projects, and produce creative media.",
5
5
  "author": {
6
6
  "name": "Makaron AI",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "makaron-cli",
3
- "version": "0.8.4",
3
+ "version": "0.9.0",
4
4
  "description": "AI image editing, video generation, and music creation via CLI. Agents can self-register, create projects, and produce creative media.",
5
5
  "displayName": "Makaron",
6
6
  "shortDescription": "AI image/video/music creation from the terminal",
package/README.md CHANGED
@@ -75,6 +75,10 @@ npx makaron-cli responses get $RUN_ID --wait --json
75
75
 
76
76
  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.
77
77
 
78
+ ```bash
79
+ npx makaron-cli chat --help
80
+ ```
81
+
78
82
  ### Submit a request
79
83
 
80
84
  ```bash
@@ -91,6 +95,18 @@ Returns immediately:
91
95
  {"runId": "xxx", "projectId": "...", "projectUrl": "https://www.makaron.app/projects/...", "status": "running"}
92
96
  ```
93
97
 
98
+ ### Common workflows
99
+
100
+ | What you want | Example |
101
+ |--------------|---------|
102
+ | Edit an image | `npx makaron-cli chat --project <id> --image photo.jpg "remove the person in the background"` |
103
+ | Generate an image | `npx makaron-cli chat --project auto "generate a cinematic poster of a rainy Tokyo alley"` |
104
+ | Make a video from the current project | `npx makaron-cli chat --project <id> "make this into a 5 second cinematic video"` |
105
+ | Fix one moment in a video from a screenshot | `npx makaron-cli chat --project <id> --image screenshot.png "@4 this frame should be Paris; only fix this moment"` |
106
+ | Cut or assemble video | `npx makaron-cli chat --project <id> --video clip.mp4 "cut out the dead air and keep the best 20 seconds"` |
107
+ | Add music | `npx makaron-cli chat --project <id> "add calm piano background music"` |
108
+ | Create motion design | `npx makaron-cli chat --project <id> "make an animated Instagram story with this image"` |
109
+
94
110
  ### With additional images (existing project)
95
111
 
96
112
  ```bash
@@ -124,6 +140,18 @@ Video files are uploaded via signed URL. CLI local video uploads support `.mp4`,
124
140
  The agent understands video content natively — it can analyze scenes, edit, extend, and compose videos. Seedance video-reference editing is still limited to ~15s provider references, so longer uploaded videos should be split/prepared by the agent before model submission; Kling remains the base/direct edit path.
125
141
  Use `chat --project <id|auto> --video ...` for any project/timeline video work. Direct `video create` is standalone and does not write timeline entries.
126
142
 
143
+ ### Fix one video moment from a screenshot
144
+
145
+ When a video is mostly good but one moment needs a local fix, attach a screenshot of the problem frame and describe the correction in normal language:
146
+
147
+ ```bash
148
+ npx makaron-cli chat --project <id> \
149
+ --image screenshot.png \
150
+ "@4 this frame should be Paris, keep the same style and only fix this moment"
151
+ ```
152
+
153
+ Makaron can locate the screenshot in the video, regenerate only the nearby segment, and then print a `Next steps` command when the new clip should be stitched back into the full MP4.
154
+
127
155
  ### Check status (single query)
128
156
 
129
157
  ```bash
@@ -170,15 +198,14 @@ npx makaron-cli edit --image photo.jpg "add cinematic warm lighting"
170
198
  # Text-to-image (no input)
171
199
  npx makaron-cli edit "a cyberpunk cityscape at night"
172
200
 
173
- # With model/skill/reference
174
- npx makaron-cli edit --image photo.jpg --model openai --skill captions "add title"
201
+ # With model/reference
175
202
  npx makaron-cli edit --image photo.jpg --ref style.jpg "match this style"
176
203
 
177
204
  # Output to file
178
205
  npx makaron-cli edit --image photo.jpg --out result.jpg "make it dramatic"
179
206
  ```
180
207
 
181
- Options: `--image`, `--model gemini|qwen|openai|pony|wai`, `--skill enhance|creative|wild|captions`, `--ref <file>` (up to 3), `--aspect <ratio>`, `--out <path>`
208
+ Options: `--image`, `--model gemini|qwen|openai|pony|wai`, `--ref <file>` (up to 3), `--aspect <ratio>`, `--out <path>`
182
209
 
183
210
  ### `video` — Standalone video tools (no project timeline)
184
211
 
@@ -191,10 +218,11 @@ npx makaron-cli analyze --video input.mp4 "describe the key actions and pacing"
191
218
 
192
219
  # 3a. Submit image-to-video rendering (images must be public URLs from step 1 or uploaded)
193
220
  npx makaron-cli video create --script "Shot 1 (5s): <<<image_1>>> ..." --image https://...jpg --duration 5 --model kling
221
+ npx makaron-cli video create --script "Shot 1 (5s): <<<image_1>>> slow cinematic push-in with native ambience" --image https://...jpg --duration 5 --model grok
194
222
 
195
223
  # 3b. Edit a video from a local file or public URL
196
- npx makaron-cli video create --script "make it funny" --video input.mp4 --duration 5 --model seedance
197
- npx makaron-cli video create --script "make it warmer and cinematic" --video https://example.com/input.mp4 --duration 5 --model seedance
224
+ npx makaron-cli video create --script "make it funny" --video input.mp4 --duration 5 --model seedance-fast
225
+ npx makaron-cli video create --script "make it warmer and cinematic" --video https://example.com/input.mp4 --duration 5 --model seedance --video-resolution 1080p
198
226
 
199
227
  # 4. Check status
200
228
  npx makaron-cli video status <taskId>
@@ -206,9 +234,9 @@ For project/timeline video editing, use:
206
234
  npx makaron-cli chat --project <id|auto> --video input.mp4 -b "make it funny"
207
235
  ```
208
236
 
209
- Options for `video create`: `--script "..."`, `--script-file <path>`, `--image <url>` (repeatable, up to 7), `--video <file|url>`, `--duration <seconds>`, `--aspect 9:16|16:9|1:1`, `--model kling|seedance`. SeeDance accepts integer output duration 4-15s (default 5s); Kling supports 5-15s.
237
+ Options for `video create`: `--script "..."`, `--script-file <path>`, `--image <url>` (repeatable, up to 7), `--video <file|url>`, `--duration <seconds>`, `--aspect 9:16|16:9|1:1`, `--model seedance-fast|seedance|kling|grok`, `--video-resolution auto|480p|720p|1080p|4k`. Default model is `seedance-fast`. SeeDance accepts integer output duration 4-15s (default 5s); Kling supports 5-15s; Grok 1.5 supports 1-15s single-image-to-video only. For `--model grok`, forced `--aspect` is ignored to avoid xAI stretching the source image; pad/create the image at the target shape first or use another model.
210
238
 
211
- Video edit model behavior: `--model kling --video` uses Kling base/direct edit internally; `--model seedance --video` uses the Seedance video-reference path and requires target <=15s, <=50MB, width/height 300-6000px, aspect ratio 0.4-2.5, and frame pixels 409,600-2,086,876. Tiny metadata padding up to 15.5s is accepted and output duration is clamped to 15s.
239
+ Video edit model behavior: `--model kling --video` uses Kling base/direct edit internally; `--model seedance-fast --video` or `--model seedance --video` uses the SeeDance video-reference path and requires target <=15s, <=50MB, width/height 300-6000px, aspect ratio 0.4-2.5, and frame pixels 409,600-2,086,876. Tiny metadata padding up to 15.5s is accepted and output duration is clamped to 15s. Grok does not support video references.
212
240
 
213
241
  ### `music` — Music generation
214
242
 
@@ -237,8 +265,15 @@ type MakaronOutput =
237
265
  | { id: string; type: "text"; status: "completed"; content: string }
238
266
  | { id: string; type: "image"; status: "completed"; url: string; snapshot_id: string }
239
267
  | { id: string; type: "design"; status: "completed"; url: string; width: number; height: number; animated: boolean; duration?: number }
240
- | { id: string; type: "video"; status: "queued"|"rendering"|"completed"|"failed"; task_id: string; url?: string; elapsed_seconds?: number }
268
+ | { id: string; type: "video"; status: "queued"|"rendering"|"completed"|"failed"; task_id: string; snapshot_id?: string; url?: string; elapsed_seconds?: number; width?: number; height?: number; error?: string; completion_actions?: CompletionAction[] }
241
269
  | { id: string; type: "music"; status: "queued"|"rendering"|"completed"|"failed"; task_id: string; url?: string; elapsed_seconds?: number }
270
+
271
+ type CompletionAction = {
272
+ label: string
273
+ prompt: string
274
+ description?: string
275
+ policy?: "confirm" | "auto"
276
+ }
242
277
  ```
243
278
 
244
279
  ## Polling Rules
@@ -247,6 +282,7 @@ type MakaronOutput =
247
282
  2. Use `next_poll_after_ms` as interval (default 5000ms)
248
283
  3. Stop when `status` is `"completed"`, `"failed"`, or `"aborted"`
249
284
  4. Top-level `status: "completed"` means ALL artifacts are ready (including rendered videos)
285
+ 5. If an async video fails, top-level `status` is `"failed"` and the failed video may include `completion_actions` for a safe retry or diagnosis. Agents can surface these as the next user-confirmed step.
250
286
 
251
287
  ## Exit Codes
252
288
 
@@ -266,6 +302,7 @@ type MakaronOutput =
266
302
  | Text-to-image | "generate a cyberpunk cityscape" |
267
303
  | Video from image | "create a 5 second video of her walking" |
268
304
  | Video with model | "use seedance model, make a 5s video" |
305
+ | Real MP4 edits | `--video clip.mp4 "trim this to the best 20 seconds and preserve audio"` |
269
306
  | **Edit video** | **"put Iron Man armor on me in this video"** |
270
307
  | **Compose videos** | **"combine @1 and @2 into one party video"** |
271
308
  | **Extend video** | **"continue the story for 10 more seconds"** |
@@ -314,12 +351,13 @@ send_message "All done!"
314
351
  - One project = one conversation thread. All history is preserved.
315
352
  - One run at a time per project. New message interrupts previous run.
316
353
  - Multi-image: `create --image a.jpg --image b.jpg` or `chat --image ref.jpg`.
317
- - Videos take 2-5 minutes to render. Use `responses get <runId> --wait --json` for the default customer-service path.
354
+ - Most videos take 3-5 minutes; Grok is usually around 30-40 seconds. Use `responses get <runId> --wait --json` for the default customer-service path.
318
355
  - Music takes ~60 seconds. Appears in output when done.
319
356
  - Images are typically ready in 15-30 seconds.
320
357
  - stdout is always machine-readable JSON/text. Human-friendly logs go to stderr.
321
358
  - Always use `chat` as the primary interface — even for single image edits.
322
359
  - `edit`/`video`/`music` are fallback tools for when `chat` is unavailable or you need raw model access without project context.
360
+ - The CLI checks npm for updates at most once per day and prints update notices to stderr. Set `MAKARON_DISABLE_UPDATE_CHECK=1` to disable it.
323
361
 
324
362
  ## Admin: Skill Marketplace Operations
325
363
 
package/bin/makaron.mjs CHANGED
@@ -19,9 +19,13 @@ import { execFileSync } from 'child_process';
19
19
  // ─── Config ──────────────────────────────────────────────────────────────────
20
20
 
21
21
  const AUTH_FILE = path.join(process.env.HOME || '~', '.makaron', 'auth.json');
22
+ const UPDATE_CHECK_FILE = path.join(process.env.HOME || '~', '.makaron', 'update-check.json');
22
23
  const DEFAULT_URL = 'https://www.makaron.app';
23
24
  const BASE_URL = process.env.MAKARON_URL || DEFAULT_URL;
24
25
  const APP_URL = process.env.MAKARON_APP_URL || DEFAULT_URL;
26
+ const NPM_PACKAGE_NAME = 'makaron-cli';
27
+ const UPDATE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
28
+ const UPDATE_CHECK_TIMEOUT_MS = 400;
25
29
 
26
30
  // Public anon key (safe to embed — only enables auth, not data access)
27
31
  const SUPABASE_URL = 'https://sdyrtztrjgmmpnirswxt.supabase.co';
@@ -49,6 +53,84 @@ function getCliVersion() {
49
53
  }
50
54
  }
51
55
 
56
+ function compareVersions(a, b) {
57
+ const parse = (version) => String(version || '')
58
+ .split('-')[0]
59
+ .split('.')
60
+ .map(part => Number.parseInt(part, 10) || 0);
61
+ const left = parse(a);
62
+ const right = parse(b);
63
+ for (let i = 0; i < Math.max(left.length, right.length); i++) {
64
+ const diff = (left[i] || 0) - (right[i] || 0);
65
+ if (diff !== 0) return diff > 0 ? 1 : -1;
66
+ }
67
+ return 0;
68
+ }
69
+
70
+ function readUpdateCache() {
71
+ try {
72
+ return JSON.parse(fs.readFileSync(UPDATE_CHECK_FILE, 'utf-8'));
73
+ } catch {
74
+ return null;
75
+ }
76
+ }
77
+
78
+ function writeUpdateCache(data) {
79
+ try {
80
+ const dir = path.dirname(UPDATE_CHECK_FILE);
81
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
82
+ fs.writeFileSync(UPDATE_CHECK_FILE, JSON.stringify(data, null, 2));
83
+ } catch { /* best effort */ }
84
+ }
85
+
86
+ function shouldCheckForUpdates(command, args) {
87
+ if (!command || command === '--version' || command === '-v' || command === 'version') return false;
88
+ if (args.includes('--help') || args.includes('-h')) return false;
89
+ if (args.includes('--json') || args.includes('--jsonl') || args.includes('--pick')) return false;
90
+ if (process.env.CI || process.env.NO_UPDATE_NOTIFIER || process.env.MAKARON_DISABLE_UPDATE_CHECK) return false;
91
+ return true;
92
+ }
93
+
94
+ async function maybeNotifyUpdate(command, args) {
95
+ if (!shouldCheckForUpdates(command, args)) return;
96
+ const currentVersion = getCliVersion();
97
+ const now = Date.now();
98
+ const cache = readUpdateCache();
99
+ if (cache?.checkedAt && now - cache.checkedAt < UPDATE_CHECK_INTERVAL_MS) {
100
+ if (cache.latestVersion && compareVersions(cache.latestVersion, currentVersion) > 0) {
101
+ printUpdateNotice(currentVersion, cache.latestVersion);
102
+ }
103
+ return;
104
+ }
105
+
106
+ const controller = new AbortController();
107
+ const timer = setTimeout(() => controller.abort(), UPDATE_CHECK_TIMEOUT_MS);
108
+ try {
109
+ const res = await fetch(`https://registry.npmjs.org/${NPM_PACKAGE_NAME}/latest`, {
110
+ signal: controller.signal,
111
+ headers: { 'Accept': 'application/json' },
112
+ });
113
+ if (!res.ok) return;
114
+ const data = await res.json();
115
+ const latestVersion = data?.version;
116
+ if (!latestVersion) return;
117
+ writeUpdateCache({ checkedAt: now, latestVersion });
118
+ if (compareVersions(latestVersion, currentVersion) > 0) {
119
+ printUpdateNotice(currentVersion, latestVersion);
120
+ }
121
+ } catch {
122
+ writeUpdateCache({ checkedAt: now, latestVersion: cache?.latestVersion || currentVersion });
123
+ } finally {
124
+ clearTimeout(timer);
125
+ }
126
+ }
127
+
128
+ function printUpdateNotice(currentVersion, latestVersion) {
129
+ process.stderr.write(`\nUpdate available: makaron-cli ${currentVersion} -> ${latestVersion}\n`);
130
+ process.stderr.write('Run: npm install -g makaron-cli@latest\n');
131
+ process.stderr.write('Or: npx makaron-cli@latest ...\n\n');
132
+ }
133
+
52
134
  function formatSeconds(seconds) {
53
135
  if (!Number.isFinite(seconds)) return String(seconds);
54
136
  return Number.isInteger(seconds) ? String(seconds) : seconds.toFixed(1).replace(/\.0$/, '');
@@ -153,6 +235,81 @@ function normalizeRunResponse(data) {
153
235
  return data;
154
236
  }
155
237
 
238
+ function collectCompletionActions(data) {
239
+ const items = [];
240
+ const add = (action, source) => {
241
+ if (!action?.label || !action?.prompt) return;
242
+ const key = `${action.label}\n${action.prompt}`;
243
+ if (items.some(i => i.key === key)) return;
244
+ items.push({ key, label: action.label, prompt: action.prompt, description: action.description, source });
245
+ };
246
+ for (const out of data.output || []) {
247
+ for (const action of out.completion_actions || out.completionActions || []) add(action, out.id || out.task_id);
248
+ }
249
+ for (const video of data.result?.videos || []) {
250
+ for (const action of video.completion_actions || video.completionActions || []) add(action, video.taskId);
251
+ }
252
+ return items;
253
+ }
254
+
255
+ function printCompletionActions(data) {
256
+ const projectId = data.projectId || data.project_id;
257
+ const actions = collectCompletionActions(data);
258
+ if (!projectId || actions.length === 0) return;
259
+ process.stderr.write('\nNext steps:\n');
260
+ for (const action of actions) {
261
+ process.stderr.write(`• ${action.label}${action.description ? ` — ${action.description}` : ''}\n`);
262
+ process.stderr.write(` makaron chat --project ${projectId} ${JSON.stringify(action.prompt)}\n`);
263
+ }
264
+ }
265
+
266
+ function printChatHelp() {
267
+ console.log(`Makaron chat — create and edit with Makaron Agent
268
+
269
+ Usage:
270
+ makaron chat --project <id|auto> [options] "your message"
271
+
272
+ Options:
273
+ --project <id|auto> Project to work in. Use "auto" to create one.
274
+ --image <file|url> Attach a reference image or screenshot. Repeatable.
275
+ --video <file|url> Attach a video to the project timeline. Repeatable.
276
+ --model <name> Preferred image/model route.
277
+ --video-model <name> Preferred video model: seedance-fast, seedance, kling, or grok.
278
+ --video-resolution <res> Video resolution: auto, 480p, 720p, 1080p, or 4k.
279
+ --background, -b Submit and print a runId.
280
+ --json Output structured JSON.
281
+ --stream Legacy live SSE stream.
282
+ --help, -h Show this help.
283
+
284
+ What you can ask:
285
+ Image edit
286
+ makaron chat --project <id> --image photo.jpg "remove the person in the background"
287
+
288
+ Image generation
289
+ makaron chat --project auto "generate a cinematic poster of a rainy Tokyo alley"
290
+
291
+ Video from image or timeline
292
+ makaron chat --project <id> "make this into a 5 second cinematic video"
293
+
294
+ Fix one video moment from a screenshot
295
+ makaron chat --project <id> --image screenshot.png "@4 this frame should be Paris; only fix this moment"
296
+
297
+ Video cuts and assembly
298
+ makaron chat --project <id> --video clip.mp4 "cut out the dead air and keep the best 20 seconds"
299
+
300
+ Music
301
+ makaron chat --project <id> "add calm piano background music"
302
+
303
+ Motion design
304
+ makaron chat --project <id> "make an animated Instagram story with this image"
305
+
306
+ After async generation:
307
+ The CLI waits for video/music tasks. If the result has a natural next step, it prints:
308
+ Next steps:
309
+ makaron chat --project <id> "..."
310
+ `);
311
+ }
312
+
156
313
  // ─── SSE Consumer ────────────────────────────────────────────────────────────
157
314
 
158
315
  async function abortRun(baseUrl, headers, runId) {
@@ -285,6 +442,7 @@ async function submitRun(baseUrl, headers, projectId, prompt, opts = {}) {
285
442
  const body = { projectId, prompt };
286
443
  if (opts.preferredModel) body.preferredModel = opts.preferredModel;
287
444
  if (opts.videoModel) body.videoModel = opts.videoModel;
445
+ if (opts.videoResolution) body.videoResolution = opts.videoResolution;
288
446
  if (opts.currentSnapshotIndex != null) body.currentSnapshotIndex = opts.currentSnapshotIndex;
289
447
  if (opts.isNsfw) body.isNsfw = opts.isNsfw;
290
448
 
@@ -399,6 +557,7 @@ async function pollRun(baseUrl, headers, runId, opts = {}) {
399
557
  else if (v.status === 'failed') process.stderr.write(`🎬 Video ${v.taskId}: failed${v.error ? ` — ${v.error}` : ''}\n`);
400
558
  else process.stderr.write(`🎬 Video ${v.taskId}: ${v.status || 'submitted'}\n`);
401
559
  }
560
+ printCompletionActions(data);
402
561
  for (const m of data.result.music || []) {
403
562
  if (m.audioUrl) process.stderr.write(`🎵 Music: ${m.audioUrl}\n`);
404
563
  else process.stderr.write(`🎵 Music ${m.taskId}: ${m.status || 'submitted'}\n`);
@@ -426,6 +585,12 @@ function applyPick(data, field) {
426
585
  case 'design_urls': return (data.output || []).filter(o => o.type === 'design' && o.url).map(o => o.url);
427
586
  case 'first_music_url': return data.output?.find(o => o.type === 'music' && o.url)?.url || null;
428
587
  case 'music_urls': return (data.output || []).filter(o => o.type === 'music' && o.url).map(o => o.url);
588
+ case 'next_steps': return collectCompletionActions(data).map(action => ({
589
+ label: action.label,
590
+ prompt: action.prompt,
591
+ description: action.description,
592
+ source: action.source,
593
+ }));
429
594
  case 'project_url': return data.project_url || data.projectUrl || null;
430
595
  case 'output': return data.output || [];
431
596
  case 'text': return data.output?.find(o => o.type === 'text')?.content || null;
@@ -950,12 +1115,12 @@ function printHelp(topic, subtopic) {
950
1115
  console.log('Usage: makaron analyze --video <file|url> ["question"]');
951
1116
  } else if (topic === 'video') {
952
1117
  if (subtopic === 'script') console.log('Usage: makaron video script --image <file> [--image <file>] [--lang en|zh] "direction"');
953
- else if (subtopic === 'create') console.log('Usage: makaron video create --script "..." (--image <url> | --video <public-url>) [--duration 10] [--aspect 9:16] [--model kling|seedance] [--keep-original-sound]');
1118
+ else if (subtopic === 'create') console.log('Usage: makaron video create --script "..." (--image <url> | --video <public-url>) [--duration 10] [--aspect 9:16] [--model seedance-fast|seedance|kling|grok] [--video-resolution auto|480p|720p|1080p|4k] [--keep-original-sound]');
954
1119
  else if (subtopic === 'status') console.log('Usage: makaron video status <taskId> | --snapshot <snapshotId> [--wait]');
955
1120
  else console.log(`Video commands:
956
1121
  video script --image <file> [--image <file>] "direction" Write video script
957
1122
  video create --script "..." --image <url> [--duration 10] Submit video task
958
- video create --script "..." --video <public-url> [--model kling|seedance] Edit a video (standalone)
1123
+ video create --script "..." --video <public-url> [--model seedance-fast|seedance|kling] Edit a video (standalone; Grok does not support video refs)
959
1124
  video status <taskId> Check video status
960
1125
  video status --snapshot <snapshotId> [--wait] Check v2 video snapshot
961
1126
  `);
@@ -995,6 +1160,8 @@ function printHelp(topic, subtopic) {
995
1160
  const args = process.argv.slice(2);
996
1161
  const command = args[0];
997
1162
 
1163
+ await maybeNotifyUpdate(command, args);
1164
+
998
1165
  if (!command || command === '--help' || command === '-h' || command === 'help') {
999
1166
  printRootHelp();
1000
1167
  } else if (hasHelpFlag(args)) {
@@ -1019,7 +1186,10 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
1019
1186
  }
1020
1187
  await createProject(baseUrl, headers, opts);
1021
1188
  } else if (command === 'chat') {
1022
- const { headers, baseUrl } = getAuth();
1189
+ if (args.includes('--help') || args.includes('-h')) {
1190
+ printChatHelp();
1191
+ process.exit(0);
1192
+ }
1023
1193
  let projectId = null;
1024
1194
  const chatImages = [];
1025
1195
  const chatVideos = [];
@@ -1027,24 +1197,30 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
1027
1197
  let useStream = false;
1028
1198
  let background = false;
1029
1199
  let jsonOutput = false;
1200
+ let activeSkill = undefined;
1030
1201
  let videoModel = undefined;
1202
+ let videoResolution = undefined;
1031
1203
  let preferredModel = undefined;
1032
1204
  for (let i = 1; i < args.length; i++) {
1033
1205
  if (args[i] === '--project' && args[i + 1]) projectId = args[++i];
1034
1206
  else if (args[i] === '--image' && args[i + 1]) chatImages.push(args[++i]);
1035
1207
  else if (args[i] === '--video' && args[i + 1]) chatVideos.push(args[++i]);
1208
+ else if (args[i] === '--skill' && args[i + 1]) activeSkill = args[++i];
1036
1209
  else if (args[i] === '--stream') useStream = true;
1037
1210
  else if (args[i] === '--background' || args[i] === '-b') background = true;
1038
1211
  else if (args[i] === '--json') jsonOutput = true;
1039
1212
  else if (args[i] === '--video-model' && args[i + 1]) videoModel = args[++i];
1213
+ else if (args[i] === '--video-resolution' && args[i + 1]) videoResolution = args[++i];
1040
1214
  else if (args[i] === '--model' && args[i + 1]) preferredModel = args[++i];
1041
1215
  else promptParts.push(args[i]);
1042
1216
  }
1043
1217
  const prompt = promptParts.join(' ');
1044
1218
  if (!prompt) {
1045
- console.error('Usage: makaron chat --project <id|auto> [--image <file>] [--video <file|url>] [--stream] [--background|-b] [--json] "your message"');
1219
+ console.error('Usage: makaron chat --project <id|auto> [options] "your message"');
1220
+ console.error('Run: makaron chat --help');
1046
1221
  process.exit(1);
1047
1222
  }
1223
+ const { headers, baseUrl } = getAuth();
1048
1224
  // Split images into URLs vs local files
1049
1225
  const imageUrlList = chatImages.filter(p => p.startsWith('http://') || p.startsWith('https://'));
1050
1226
  const imageFileList = chatImages.filter(p => !p.startsWith('http://') && !p.startsWith('https://'));
@@ -1122,7 +1298,7 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
1122
1298
  }
1123
1299
 
1124
1300
  // Upload videos to project timeline (via /api/projects/create with videoUrls)
1125
- let finalPrompt = prompt;
1301
+ let finalPrompt = activeSkill ? `[Active skill: ${activeSkill}]\n${prompt}` : prompt;
1126
1302
  if (chatVideos.length > 0) {
1127
1303
  // Upload local files via signed URL (no size limit, works with API key auth)
1128
1304
  const uploadedVideoUrls = [...prevalidatedVideoUrlList];
@@ -1166,7 +1342,7 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
1166
1342
 
1167
1343
  // Inject hint so Agent knows videos are available
1168
1344
  const hint = `[User uploaded ${chatVideos.length === 1 ? 'a video' : `${chatVideos.length} videos`}. Use analyze_video to understand the content.]`;
1169
- finalPrompt = `${prompt}\n\n${hint}`;
1345
+ finalPrompt = `${finalPrompt}\n\n${hint}`;
1170
1346
  }
1171
1347
 
1172
1348
  if (useStream) {
@@ -1180,7 +1356,7 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
1180
1356
  for (const task of results.musicTasks) await pollMusic(baseUrl, headers, task.taskId);
1181
1357
  } else {
1182
1358
  // Default: fire-and-forget + poll
1183
- const { runId } = await submitRun(baseUrl, headers, projectId, finalPrompt, { videoModel, preferredModel });
1359
+ const { runId } = await submitRun(baseUrl, headers, projectId, finalPrompt, { videoModel, videoResolution, preferredModel });
1184
1360
  if (background) {
1185
1361
  // Just print runId and exit
1186
1362
  if (jsonOutput) {
@@ -1311,7 +1487,7 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
1311
1487
  else promptParts.push(args[i]);
1312
1488
  }
1313
1489
  editArgs.editPrompt = promptParts.join(' ');
1314
- if (!editArgs.editPrompt) { console.error('Usage: makaron edit [--image <file|url>] [--model gemini|qwen|openai] [--skill enhance|creative|wild|captions] [--ref <file>] [--out <file>] "prompt"'); process.exit(1); }
1490
+ if (!editArgs.editPrompt) { console.error('Usage: makaron edit [--image <file|url>] [--model gemini|qwen|openai] [--ref <file>] [--out <file>] "prompt"'); process.exit(1); }
1315
1491
  process.stderr.write('🎨 Generating...\n');
1316
1492
  const result = await callMcpTool(baseUrl, headers, 'makaron_edit_image', editArgs);
1317
1493
  saveMcpImage(result, outputPath);
@@ -1347,7 +1523,7 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
1347
1523
 
1348
1524
  } else if (sub === 'create') {
1349
1525
  const images = [];
1350
- let script = '', duration = undefined, aspectRatio = undefined, videoModel = undefined, wait = false;
1526
+ let script = '', duration = undefined, aspectRatio = undefined, videoModel = undefined, videoResolution = undefined, wait = false;
1351
1527
  let video = null, keepOriginalSound = false;
1352
1528
  for (let i = 2; i < args.length; i++) {
1353
1529
  if (args[i] === '--image' && args[i + 1]) images.push(args[++i]);
@@ -1357,6 +1533,8 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
1357
1533
  else if (args[i] === '--duration' && args[i + 1]) duration = Number(args[++i]);
1358
1534
  else if (args[i] === '--aspect' && args[i + 1]) aspectRatio = args[++i];
1359
1535
  else if (args[i] === '--model' && args[i + 1]) videoModel = args[++i];
1536
+ else if (args[i] === '--video-resolution' && args[i + 1]) videoResolution = args[++i];
1537
+ else if (args[i] === '--resolution' && args[i + 1]) videoResolution = args[++i];
1360
1538
  else if (args[i] === '--keep-original-sound') keepOriginalSound = true;
1361
1539
  else if (args[i] === '--project') {
1362
1540
  console.error('Usage: video create no longer supports --project. Use: makaron chat --project <id> --video <file|url> "your request"');
@@ -1365,7 +1543,7 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
1365
1543
  else if (args[i] === '--wait') wait = true;
1366
1544
  }
1367
1545
  if ((!images.length && !video) || !script) {
1368
- console.error('Usage: makaron video create --script "..." (--image <url> | --video <public-url>) [--duration 10] [--aspect 9:16] [--model kling|seedance] [--keep-original-sound]');
1546
+ console.error('Usage: makaron video create --script "..." (--image <url> | --video <public-url>) [--duration 10] [--aspect 9:16] [--model seedance-fast|seedance|kling|grok] [--video-resolution auto|480p|720p|1080p|4k] [--keep-original-sound]');
1369
1547
  process.exit(1);
1370
1548
  }
1371
1549
 
@@ -1376,15 +1554,15 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
1376
1554
 
1377
1555
  let videoUrl = isHttpUrl(video) ? video : null;
1378
1556
  let inputVideoMeta = null;
1379
- const selectedVideoModel = videoModel || 'kling';
1557
+ const selectedVideoModel = videoModel || 'seedance-fast';
1380
1558
  if (videoUrl) {
1381
- process.stderr.write(`📹 Assuming public video URL already matches provider reference limits. Seedance requires ≤${MAX_VIDEO_PROVIDER_REFERENCE_DURATION}s, ≤50MB, sides 300-6000px, frame pixels 409,600-${MAX_VIDEO_FRAME_PIXELS}; Kling requires ≤200MB and ≤2K.\n`);
1559
+ process.stderr.write(`📹 Assuming public video URL already matches provider reference limits. Seedance requires ≤${MAX_VIDEO_PROVIDER_REFERENCE_DURATION}s, ≤50MB, sides 300-6000px, frame pixels 409,600-${MAX_VIDEO_FRAME_PIXELS}; Kling requires ≤200MB and ≤2K. Grok does not support video references.\n`);
1382
1560
  }
1383
1561
  if (video && !videoUrl) {
1384
1562
  const valid = validateVideoFile(video, {
1385
1563
  maxDuration: MAX_VIDEO_PROVIDER_REFERENCE_DURATION,
1386
1564
  durationTolerance: MAX_VIDEO_PROVIDER_REFERENCE_DURATION_TOLERANCE,
1387
- ...(selectedVideoModel === 'seedance' ? {
1565
+ ...(selectedVideoModel === 'seedance' || selectedVideoModel === 'seedance-fast' ? {
1388
1566
  minFramePixels: SEEDANCE_MIN_VIDEO_FRAME_PIXELS,
1389
1567
  minSide: SEEDANCE_MIN_VIDEO_SIDE,
1390
1568
  maxSide: SEEDANCE_MAX_VIDEO_SIDE,
@@ -1402,12 +1580,11 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
1402
1580
  // Standalone MCP tool (no project timeline write)
1403
1581
  process.stderr.write('🎬 Submitting video...\n');
1404
1582
  const vArgs = videoUrl
1405
- ? { videoUrl, editPrompt: script, images, videoModel: selectedVideoModel, referType: selectedVideoModel === 'seedance' ? 'feature' : 'base' }
1406
- : { script, images };
1583
+ ? { videoUrl, editPrompt: script, images, videoModel: selectedVideoModel, videoResolution, referType: (selectedVideoModel === 'seedance' || selectedVideoModel === 'seedance-fast') ? 'feature' : 'base' }
1584
+ : { script, images, videoModel: selectedVideoModel, videoResolution };
1407
1585
  const effectiveDuration = duration || (inputVideoMeta?.duration ? Math.min(MAX_VIDEO_PROVIDER_REFERENCE_DURATION, Math.round(inputVideoMeta.duration)) : undefined);
1408
1586
  if (effectiveDuration) vArgs.duration = effectiveDuration;
1409
1587
  if (aspectRatio) vArgs.aspectRatio = aspectRatio;
1410
- if (videoModel && !videoUrl) vArgs.videoModel = videoModel;
1411
1588
  if (keepOriginalSound && videoUrl) vArgs.keepOriginalSound = true;
1412
1589
  const result = await callMcpTool(baseUrl, headers, videoUrl ? 'makaron_edit_video' : 'makaron_create_video', vArgs);
1413
1590
  const text = result?.content?.find(c => c.type === 'text')?.text;
@@ -1453,7 +1630,7 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
1453
1630
  console.log(`Video commands:
1454
1631
  video script --image <file> [--image <file>] "direction" Write video script
1455
1632
  video create --script "..." --image <url> [--duration 10] Submit video task
1456
- video create --script "..." --video <public-url> [--model kling|seedance] Edit a video (standalone)
1633
+ video create --script "..." --video <public-url> [--model seedance-fast|seedance|kling] Edit a video (standalone; Grok does not support video refs)
1457
1634
  video status <taskId> Check video status
1458
1635
  video status --snapshot <snapshotId> [--wait] Check v2 video snapshot
1459
1636
  `);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "makaron-cli",
3
- "version": "0.8.4",
3
+ "version": "0.10.0",
4
4
  "description": "Talk to Makaron Agent from the terminal — create projects, edit images, generate videos",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -16,7 +16,6 @@
16
16
  "skills/",
17
17
  ".codex-plugin/",
18
18
  ".claude-plugin/",
19
- "SKILL.md",
20
19
  "README.md"
21
20
  ],
22
21
  "keywords": [
@@ -1,6 +1,6 @@
1
1
  ---
2
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.
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, video editing, music composition, or design creation. Requires `npx makaron-cli` and MAKARON_API_KEY env var.
4
4
  ---
5
5
 
6
6
  # Makaron CLI — Agent Integration Skill
@@ -64,6 +64,10 @@ npx makaron-cli responses get $RUN_ID --wait --json
64
64
 
65
65
  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.
66
66
 
67
+ ```bash
68
+ npx makaron-cli chat --help
69
+ ```
70
+
67
71
  ### Submit a request
68
72
 
69
73
  ```bash
@@ -80,6 +84,18 @@ Returns immediately:
80
84
  {"runId": "xxx", "projectId": "...", "projectUrl": "https://www.makaron.app/projects/...", "status": "running"}
81
85
  ```
82
86
 
87
+ ### Common workflows
88
+
89
+ | What you want | Example |
90
+ |--------------|---------|
91
+ | Edit an image | `npx makaron-cli chat --project <id> --image photo.jpg "remove the person in the background"` |
92
+ | Generate an image | `npx makaron-cli chat --project auto "generate a cinematic poster of a rainy Tokyo alley"` |
93
+ | Make a video from the current project | `npx makaron-cli chat --project <id> "make this into a 5 second cinematic video"` |
94
+ | Fix one moment in a video from a screenshot | `npx makaron-cli chat --project <id> --image screenshot.png "@4 this frame should be Paris; only fix this moment"` |
95
+ | Cut or assemble video | `npx makaron-cli chat --project <id> --video clip.mp4 "cut out the dead air and keep the best 20 seconds"` |
96
+ | Add music | `npx makaron-cli chat --project <id> "add calm piano background music"` |
97
+ | Create motion design | `npx makaron-cli chat --project <id> "make an animated Instagram story with this image"` |
98
+
83
99
  ### With additional images (existing project)
84
100
 
85
101
  ```bash
@@ -116,6 +132,18 @@ Supported formats: MP4, MOV, WebM. CLI local video uploads support max 50MB, max
116
132
 
117
133
  Use `chat --project <id|auto> --video ...` for any project/timeline video work. Direct video commands are standalone raw-tool calls.
118
134
 
135
+ ### Fix one video moment from a screenshot
136
+
137
+ When a video is mostly good but one moment needs a local fix, attach a screenshot of the problem frame and describe the correction in normal language:
138
+
139
+ ```bash
140
+ npx makaron-cli chat --project <id> \
141
+ --image screenshot.png \
142
+ "@4 this frame should be Paris, keep the same style and only fix this moment"
143
+ ```
144
+
145
+ Makaron can locate the screenshot in the video, regenerate only the nearby segment, and then print a `Next steps` command when the new clip should be stitched back into the full MP4.
146
+
119
147
  ### Check status (single query)
120
148
 
121
149
  ```bash
@@ -162,15 +190,14 @@ npx makaron-cli edit --image photo.jpg "add cinematic warm lighting"
162
190
  # Text-to-image (no input)
163
191
  npx makaron-cli edit "a cyberpunk cityscape at night"
164
192
 
165
- # With model/skill/reference
166
- npx makaron-cli edit --image photo.jpg --model openai --skill captions "add title"
193
+ # With model/reference
167
194
  npx makaron-cli edit --image photo.jpg --ref style.jpg "match this style"
168
195
 
169
196
  # Output to file
170
197
  npx makaron-cli edit --image photo.jpg --out result.jpg "make it dramatic"
171
198
  ```
172
199
 
173
- Options: `--image`, `--model gemini|qwen|openai|pony|wai`, `--skill enhance|creative|wild|captions`, `--ref <file>` (up to 3), `--aspect <ratio>`, `--out <path>`
200
+ Options: `--image`, `--model gemini|qwen|openai|pony|wai`, `--ref <file>` (up to 3), `--aspect <ratio>`, `--out <path>`
174
201
 
175
202
  ### `video` — Standalone video tools (no project timeline)
176
203
 
@@ -185,8 +212,8 @@ npx makaron-cli analyze --video input.mp4 "describe the key actions and pacing"
185
212
  npx makaron-cli video create --script "Shot 1 (5s): <<<image_1>>> ..." --image https://...jpg --duration 5 --model kling
186
213
 
187
214
  # 3b. Edit a video from a local file or public URL
188
- npx makaron-cli video create --script "make it funny" --video input.mp4 --duration 5 --model seedance
189
- npx makaron-cli video create --script "make it warmer and cinematic" --video https://example.com/input.mp4 --duration 5 --model seedance
215
+ npx makaron-cli video create --script "make it funny" --video input.mp4 --duration 5 --model seedance-fast
216
+ npx makaron-cli video create --script "make it warmer and cinematic" --video https://example.com/input.mp4 --duration 5 --model seedance --video-resolution 1080p
190
217
 
191
218
  # 4. Check status
192
219
  npx makaron-cli video status <taskId>
@@ -198,9 +225,9 @@ npx makaron-cli video status <taskId>
198
225
  npx makaron-cli chat --project <id|auto> --video input.mp4 -b "make it funny"
199
226
  ```
200
227
 
201
- Options for `video create`: `--script "..."`, `--script-file <path>`, `--image <url>` (repeatable, up to 7), `--video <file|url>`, `--duration <seconds>`, `--aspect 9:16|16:9|1:1`, `--model kling|seedance`. SeeDance accepts integer output duration 4-15s (default 5s); Kling supports 5-15s.
228
+ Options for `video create`: `--script "..."`, `--script-file <path>`, `--image <url>` (repeatable, up to 7), `--video <file|url>`, `--duration <seconds>`, `--aspect 9:16|16:9|1:1`, `--model seedance-fast|seedance|kling|grok`, `--video-resolution auto|480p|720p|1080p|4k`. Default model is `seedance-fast`. SeeDance accepts integer output duration 4-15s (default 5s); Kling supports 5-15s; Grok 1.5 supports 1-15s single-image-to-video only. For `--model grok`, forced `--aspect` is ignored to avoid xAI stretching the source image; pad/create the image at the target shape first or use another model.
202
229
 
203
- Video edit model behavior: `--model kling --video` uses Kling base/direct edit internally; `--model seedance --video` uses the Seedance video-reference path and requires target <=15s, <=50MB, width/height 300-6000px, aspect ratio 0.4-2.5, and frame pixels 409,600-2,086,876. Tiny metadata padding up to 15.5s is accepted and output duration is clamped to 15s.
230
+ Video edit model behavior: `--model kling --video` uses Kling base/direct edit internally; `--model seedance-fast --video` or `--model seedance --video` uses the SeeDance video-reference path and requires target <=15s, <=50MB, width/height 300-6000px, aspect ratio 0.4-2.5, and frame pixels 409,600-2,086,876. Tiny metadata padding up to 15.5s is accepted and output duration is clamped to 15s. Grok does not support video references.
204
231
 
205
232
  ### `music` — Music generation
206
233
 
@@ -229,8 +256,15 @@ type MakaronOutput =
229
256
  | { id: string; type: "text"; status: "completed"; content: string }
230
257
  | { id: string; type: "image"; status: "completed"; url: string; snapshot_id: string }
231
258
  | { id: string; type: "design"; status: "completed"; url: string; width: number; height: number; animated: boolean; duration?: number }
232
- | { id: string; type: "video"; status: "queued"|"rendering"|"completed"|"failed"; task_id: string; snapshot_id?: string; url?: string; elapsed_seconds?: number; width?: number; height?: number }
259
+ | { id: string; type: "video"; status: "queued"|"rendering"|"completed"|"failed"; task_id: string; snapshot_id?: string; url?: string; elapsed_seconds?: number; width?: number; height?: number; error?: string; completion_actions?: CompletionAction[] }
233
260
  | { id: string; type: "music"; status: "queued"|"rendering"|"completed"|"failed"; task_id: string; url?: string; elapsed_seconds?: number }
261
+
262
+ type CompletionAction = {
263
+ label: string
264
+ prompt: string
265
+ description?: string
266
+ policy?: "confirm" | "auto"
267
+ }
234
268
  ```
235
269
 
236
270
  ## Polling Rules
@@ -239,6 +273,7 @@ type MakaronOutput =
239
273
  2. Use `next_poll_after_ms` as interval (default 5000ms)
240
274
  3. Stop when `status` is `"completed"`, `"failed"`, or `"aborted"`
241
275
  4. Top-level `status: "completed"` means ALL artifacts are ready (including rendered videos)
276
+ 5. If an async video fails, top-level `status` is `"failed"` and the failed video may include `completion_actions` for a safe retry or diagnosis. Agents can surface these as the next user-confirmed step.
242
277
 
243
278
  ## Exit Codes
244
279
 
@@ -258,6 +293,7 @@ type MakaronOutput =
258
293
  | Text-to-image | "generate a cyberpunk cityscape" |
259
294
  | Video from image | "create a 5 second video of her walking" |
260
295
  | Video with model | "use seedance model, make a 5s video" |
296
+ | Real MP4 edits | `--video clip.mp4 "trim this to the best 20 seconds and preserve audio"` |
261
297
  | Background music | "add calm piano music" |
262
298
  | Motion design | "create an Instagram story with animated text" |
263
299
  | Multi-step | "edit the photo then make a video from it" |
@@ -303,7 +339,7 @@ send_message "All done!"
303
339
  - One project = one conversation thread. All history is preserved.
304
340
  - One run at a time per project. New message interrupts previous run.
305
341
  - Multi-image: `create --image a.jpg --image b.jpg` or `chat --image ref.jpg`.
306
- - Videos take 2-5 minutes to render. Use `responses get <runId> --wait --json` for the default customer-service path.
342
+ - Most videos take 3-5 minutes; Grok is usually around 30-40 seconds. Use `responses get <runId> --wait --json` for the default customer-service path.
307
343
  - Music takes ~60 seconds. Appears in output when done.
308
344
  - Images are typically ready in 15-30 seconds.
309
345
  - stdout is always machine-readable JSON/text. Human-friendly logs go to stderr.
package/SKILL.md DELETED
@@ -1,311 +0,0 @@
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.app** is for humans. **makaron-cli** is for AI agents.
9
-
10
- 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.
11
-
12
- ## Setup
13
-
14
- ### Get your API key
15
-
16
- **Option A: Human login**
17
- 1. Go to [makaron.app](https://makaron.app) and log in
18
- 2. Open the menu (top-right) → **Get API Key**
19
- 3. Copy your `mk_live_...` key
20
-
21
- **Option B: Self-Registration (no human required)**
22
- ```bash
23
- # Step 1: Get challenge
24
- npx makaron-cli register --json
25
- # → { "challenge_id": "...", "challenge": "...", "expected_format": "numeric, round to 2 decimal places" }
26
-
27
- # Step 2: Solve and verify
28
- npx makaron-cli register --verify --challenge-id <id> --answer 34.5
29
- # → Key saved to ~/.makaron/auth.json
30
- # → { "api_key": "mk_live_...", "credits": N, "claim_url": "..." }
31
-
32
- # (Optional) Let a human claim this account
33
- npx makaron-cli claim
34
- # → { "claim_url": "..." } — share with human to link key to their account (valid 7 days)
35
- ```
36
-
37
- Discovery endpoint: `GET https://www.makaron.app/api/agent/register` — returns full registration flow + CLI usage as JSON.
38
-
39
- After self-registration the key is saved locally — no need to export `MAKARON_API_KEY`.
40
-
41
- ```bash
42
- export MAKARON_API_KEY=mk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
43
- ```
44
-
45
- Verify: `npx makaron-cli list` should show projects.
46
-
47
- ## Core Workflow
48
-
49
- ```bash
50
- # One-shot: create project + upload image + submit prompt — all in one command
51
- RUN_ID=$(npx makaron-cli chat --project auto --image photo.jpg -b "make it cinematic and create a 5s video")
52
-
53
- # Wait for the final customer-ready result
54
- npx makaron-cli responses get $RUN_ID --wait --json
55
- ```
56
-
57
- Or with an existing project:
58
- ```bash
59
- RUN_ID=$(npx makaron-cli chat --project $PROJECT_ID -b "make a 5s video")
60
- npx makaron-cli responses get $RUN_ID --wait --json
61
- ```
62
-
63
- ## Primary: `chat` (Agent-driven creative work)
64
-
65
- 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.
66
-
67
- ### Submit a request
68
-
69
- ```bash
70
- # With existing project
71
- npx makaron-cli chat --project <id> --json -b "<prompt>"
72
-
73
- # Auto-create project (with or without images)
74
- npx makaron-cli chat --project auto --image photo.jpg --json -b "make it cinematic"
75
- npx makaron-cli chat --project auto --image img1.jpg --image img2.jpg --json -b "combine these"
76
- ```
77
-
78
- Returns immediately:
79
- ```json
80
- {"runId": "xxx", "projectId": "...", "projectUrl": "https://www.makaron.app/projects/...", "status": "running"}
81
- ```
82
-
83
- ### With additional images (existing project)
84
-
85
- ```bash
86
- npx makaron-cli chat --project <id> --image ref1.jpg --image ref2.jpg -b "use these as style reference"
87
- ```
88
-
89
- ### Inspect existing timeline media
90
-
91
- Before starting a follow-up run on an existing project, list the current timeline media so you know what assets are available and which `<<<media_N>>>` references to use:
92
-
93
- ```bash
94
- npx makaron-cli project media <projectId> --json
95
- ```
96
-
97
- This is project-scoped. `responses get <runId> --pick output` only returns artifacts from one run; `project media` returns the whole project timeline: original uploads, references, generated images, video snapshots, and editable compositions.
98
-
99
- ### With video input (edit, compose, extend)
100
-
101
- ```bash
102
- # Upload a video and transform it — Agent understands video content natively
103
- npx makaron-cli chat --project auto --video selfie.mp4 -b "put Iron Man armor on me"
104
-
105
- # Combine a person's photo with a video scene
106
- npx makaron-cli chat --project <id> --video party.mp4 --image kid.jpg -b "make this kid join the party"
107
-
108
- # Multiple videos — compose or splice
109
- npx makaron-cli chat --project <id> --video clip1.mp4 --video clip2.mp4 -b "combine into one seamless video"
110
-
111
- # Video URL (public, downloadable)
112
- npx makaron-cli chat --project auto --video https://example.com/dance.mp4 -b "extend this to 15 seconds"
113
- ```
114
-
115
- Supported formats: MP4, MOV, WebM. CLI local video uploads support max 50MB, max 120s with 1s metadata tolerance, and <=1080p / 2,086,876 frame pixels. The frontend can transcode larger videos before upload; the CLI uploads directly to Storage and rejects videos above those limits. Videos are uploaded to the project timeline. The Agent can analyze scenes, edit content, compose multiple clips, extend duration, and add effects — all via natural language. Seedance video-reference editing is still limited to ~15s provider references, so longer uploaded videos should be split/prepared by the agent before model submission; Kling remains the base/direct edit path.
116
-
117
- Use `chat --project <id|auto> --video ...` for any project/timeline video work. Direct video commands are standalone raw-tool calls.
118
-
119
- ### Check status (single query)
120
-
121
- ```bash
122
- npx makaron-cli responses get <runId> --json
123
- ```
124
-
125
- ### Advanced: stream incremental events
126
-
127
- ```bash
128
- npx makaron-cli responses watch <runId> --jsonl
129
- ```
130
-
131
- Outputs one JSON per line as artifacts appear:
132
- ```
133
- {"event":"output.added","item":{"id":"out_1","type":"image","status":"completed","url":"https://..."}}
134
- {"event":"output.added","item":{"id":"out_2","type":"video","status":"rendering","task_id":"xxx"}}
135
- {"event":"output.updated","item":{"id":"out_2","type":"video","status":"completed","url":"https://..."}}
136
- {"event":"done","status":"completed"}
137
- ```
138
-
139
- ### Extract specific results
140
-
141
- ```bash
142
- npx makaron-cli responses get <runId> --pick first_image_url
143
- npx makaron-cli responses get <runId> --pick image_urls # all images (JSON array)
144
- npx makaron-cli responses get <runId> --pick first_video_url
145
- npx makaron-cli responses get <runId> --pick video_urls # all videos
146
- npx makaron-cli responses get <runId> --pick project_url
147
- npx makaron-cli responses get <runId> --pick text # agent's text reply
148
- npx makaron-cli responses get <runId> --pick output # full output array
149
- npx makaron-cli responses get <runId> --pick status
150
- ```
151
-
152
- ## Fallback: Direct tool calls (no project context)
153
-
154
- Use these only when `chat` is unavailable or you need raw model access without project/conversation context.
155
-
156
- ### `edit` — One-shot image editing
157
-
158
- ```bash
159
- # Edit an existing image
160
- npx makaron-cli edit --image photo.jpg "add cinematic warm lighting"
161
-
162
- # Text-to-image (no input)
163
- npx makaron-cli edit "a cyberpunk cityscape at night"
164
-
165
- # With model/skill/reference
166
- npx makaron-cli edit --image photo.jpg --model openai --skill captions "add title"
167
- npx makaron-cli edit --image photo.jpg --ref style.jpg "match this style"
168
-
169
- # Output to file
170
- npx makaron-cli edit --image photo.jpg --out result.jpg "make it dramatic"
171
- ```
172
-
173
- Options: `--image`, `--model gemini|qwen|openai|pony|wai`, `--skill enhance|creative|wild|captions`, `--ref <file>` (up to 3), `--aspect <ratio>`, `--out <path>`
174
-
175
- ### `video` — Standalone video tools (no project timeline)
176
-
177
- ```bash
178
- # 1. Write script from images
179
- npx makaron-cli video script --image img1.jpg "cinematic story"
180
-
181
- # 2. Analyze a video (standalone, no timeline write)
182
- npx makaron-cli analyze --video input.mp4 "describe the key actions and pacing"
183
-
184
- # 3a. Submit image-to-video rendering (images must be public URLs from step 1 or uploaded)
185
- npx makaron-cli video create --script "Shot 1 (5s): <<<image_1>>> ..." --image https://...jpg --duration 5 --model kling
186
-
187
- # 3b. Edit a video from a local file or public URL
188
- npx makaron-cli video create --script "make it funny" --video input.mp4 --duration 5 --model seedance
189
- npx makaron-cli video create --script "make it warmer and cinematic" --video https://example.com/input.mp4 --duration 5 --model seedance
190
-
191
- # 4. Check status
192
- npx makaron-cli video status <taskId>
193
- ```
194
-
195
- `video create` returns a provider task id and does not create or update a Makaron project timeline. For project/timeline video editing, use:
196
-
197
- ```bash
198
- npx makaron-cli chat --project <id|auto> --video input.mp4 -b "make it funny"
199
- ```
200
-
201
- Options for `video create`: `--script "..."`, `--script-file <path>`, `--image <url>` (repeatable, up to 7), `--video <file|url>`, `--duration <seconds>`, `--aspect 9:16|16:9|1:1`, `--model kling|seedance`. SeeDance accepts integer output duration 4-15s (default 5s); Kling supports 5-15s.
202
-
203
- Video edit model behavior: `--model kling --video` uses Kling base/direct edit internally; `--model seedance --video` uses the Seedance video-reference path and requires target <=15s, <=50MB, width/height 300-6000px, aspect ratio 0.4-2.5, and frame pixels 409,600-2,086,876. Tiny metadata padding up to 15.5s is accepted and output duration is clamped to 15s.
204
-
205
- ### `music` — Music generation
206
-
207
- ```bash
208
- npx makaron-cli music create "gentle piano, warm strings, cinematic"
209
- npx makaron-cli music create --vocals --style "lo-fi" "rainy day vibes"
210
- npx makaron-cli music status <taskId>
211
- ```
212
-
213
- Options: `--vocals` (include vocals), `--style "genre"`
214
-
215
- ## Response Schema
216
-
217
- ```typescript
218
- type MakaronRunResponse = {
219
- id: string
220
- status: "in_progress" | "completed" | "failed" | "aborted"
221
- incomplete: boolean // true = keep polling
222
- project_id: string
223
- project_url: string
224
- next_poll_after_ms?: number // suggested poll interval
225
- output: MakaronOutput[]
226
- }
227
-
228
- type MakaronOutput =
229
- | { id: string; type: "text"; status: "completed"; content: string }
230
- | { id: string; type: "image"; status: "completed"; url: string; snapshot_id: string }
231
- | { id: string; type: "design"; status: "completed"; url: string; width: number; height: number; animated: boolean; duration?: number }
232
- | { id: string; type: "video"; status: "queued"|"rendering"|"completed"|"failed"; task_id: string; snapshot_id?: string; url?: string; elapsed_seconds?: number; width?: number; height?: number }
233
- | { id: string; type: "music"; status: "queued"|"rendering"|"completed"|"failed"; task_id: string; url?: string; elapsed_seconds?: number }
234
- ```
235
-
236
- ## Polling Rules
237
-
238
- 1. Poll while `incomplete: true` or `status` is `"in_progress"`
239
- 2. Use `next_poll_after_ms` as interval (default 5000ms)
240
- 3. Stop when `status` is `"completed"`, `"failed"`, or `"aborted"`
241
- 4. Top-level `status: "completed"` means ALL artifacts are ready (including rendered videos)
242
-
243
- ## Exit Codes
244
-
245
- | Code | Meaning |
246
- |------|---------|
247
- | 0 | Success (completed) or valid in-progress response |
248
- | 1 | Failed, aborted, or HTTP error |
249
- | 2 | Timeout (partial response still printed to stdout) |
250
-
251
- ## What Makaron Agent Can Do
252
-
253
- | Task | Example prompt |
254
- |------|---------------|
255
- | Edit photo | "make it cinematic with warm tones" |
256
- | Style transfer | "convert to oil painting style" |
257
- | Add/remove elements | "add a cat on the table" / "remove background person" |
258
- | Text-to-image | "generate a cyberpunk cityscape" |
259
- | Video from image | "create a 5 second video of her walking" |
260
- | Video with model | "use seedance model, make a 5s video" |
261
- | Background music | "add calm piano music" |
262
- | Motion design | "create an Instagram story with animated text" |
263
- | Multi-step | "edit the photo then make a video from it" |
264
-
265
- ## Recommended Pattern: Service Flow (Feishu/OpenClaw/Group Chat)
266
-
267
- When serving end-users in a chat environment (Feishu, Slack, Discord), use this proactive message pattern:
268
-
269
- ```bash
270
- # 1. Immediately acknowledge the user
271
- send_message "Got it! Working on it now..."
272
-
273
- # 2. Create project + submit (one command)
274
- RUN_ID=$(npx makaron-cli chat --project auto --image photo.jpg -b "make it cinematic and create a 5s video")
275
-
276
- # 3. Send project link proactively
277
- PROJECT_URL=$(npx makaron-cli responses get $RUN_ID --pick project_url)
278
- send_message "Project created: $PROJECT_URL"
279
-
280
- # 4. Wait for the final customer-ready result
281
- RESULT=$(npx makaron-cli responses get $RUN_ID --wait --json)
282
- IMAGE_URLS=$(echo "$RESULT" | jq -r '[.result.images[]?.imageUrl, .output[]? | select(.type == "image") | .url] | map(select(. != null)) | unique | .[]')
283
- VIDEO_URLS=$(echo "$RESULT" | jq -r '[.result.videos[]?.videoUrl, .output[]? | select(.type == "video") | .url] | map(select(. != null)) | unique | .[]')
284
-
285
- for URL in $IMAGE_URLS; do
286
- send_image "$URL"
287
- done
288
- for URL in $VIDEO_URLS; do
289
- send_video "$URL"
290
- done
291
- send_message "All done!"
292
- ```
293
-
294
- **Key principles for service agents:**
295
- - **Proactive, not silent**: Acknowledge immediately, send the project link early, then send the final customer-ready media when the run completes.
296
- - **Media over links**: When possible, send images/videos as native media in the chat (download URL and upload as attachment), not just paste the URL.
297
- - **Immediate acknowledgment**: Reply within 1 second of receiving user request. Don't make users wait for project creation.
298
- - **Project link early**: Send the project URL right after creation so users can check anytime.
299
- - **Use `get --wait --json` as the default service path**: reserve `watch --jsonl` for advanced streaming or debugging integrations that explicitly need incremental events.
300
-
301
- ## Important Notes
302
-
303
- - One project = one conversation thread. All history is preserved.
304
- - One run at a time per project. New message interrupts previous run.
305
- - Multi-image: `create --image a.jpg --image b.jpg` or `chat --image ref.jpg`.
306
- - Videos take 2-5 minutes to render. Use `responses get <runId> --wait --json` for the default customer-service path.
307
- - Music takes ~60 seconds. Appears in output when done.
308
- - Images are typically ready in 15-30 seconds.
309
- - stdout is always machine-readable JSON/text. Human-friendly logs go to stderr.
310
- - Always use `chat` as the primary interface — even for single image edits.
311
- - `edit`/`video`/`music` are fallback tools for when `chat` is unavailable or you need raw model access without project context.