makaron-cli 0.8.4 → 0.9.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
 
@@ -237,8 +264,15 @@ type MakaronOutput =
237
264
  | { id: string; type: "text"; status: "completed"; content: string }
238
265
  | { id: string; type: "image"; status: "completed"; url: string; snapshot_id: string }
239
266
  | { 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 }
267
+ | { 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
268
  | { id: string; type: "music"; status: "queued"|"rendering"|"completed"|"failed"; task_id: string; url?: string; elapsed_seconds?: number }
269
+
270
+ type CompletionAction = {
271
+ label: string
272
+ prompt: string
273
+ description?: string
274
+ policy?: "confirm" | "auto"
275
+ }
242
276
  ```
243
277
 
244
278
  ## Polling Rules
@@ -247,6 +281,7 @@ type MakaronOutput =
247
281
  2. Use `next_poll_after_ms` as interval (default 5000ms)
248
282
  3. Stop when `status` is `"completed"`, `"failed"`, or `"aborted"`
249
283
  4. Top-level `status: "completed"` means ALL artifacts are ready (including rendered videos)
284
+ 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
285
 
251
286
  ## Exit Codes
252
287
 
@@ -266,6 +301,7 @@ type MakaronOutput =
266
301
  | Text-to-image | "generate a cyberpunk cityscape" |
267
302
  | Video from image | "create a 5 second video of her walking" |
268
303
  | Video with model | "use seedance model, make a 5s video" |
304
+ | Real MP4 edits | `--video clip.mp4 "trim this to the best 20 seconds and preserve audio"` |
269
305
  | **Edit video** | **"put Iron Man armor on me in this video"** |
270
306
  | **Compose videos** | **"combine @1 and @2 into one party video"** |
271
307
  | **Extend video** | **"continue the story for 10 more seconds"** |
@@ -320,6 +356,7 @@ send_message "All done!"
320
356
  - stdout is always machine-readable JSON/text. Human-friendly logs go to stderr.
321
357
  - Always use `chat` as the primary interface — even for single image edits.
322
358
  - `edit`/`video`/`music` are fallback tools for when `chat` is unavailable or you need raw model access without project context.
359
+ - 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
360
 
324
361
  ## Admin: Skill Marketplace Operations
325
362
 
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,80 @@ 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.
278
+ --background, -b Submit and print a runId.
279
+ --json Output structured JSON.
280
+ --stream Legacy live SSE stream.
281
+ --help, -h Show this help.
282
+
283
+ What you can ask:
284
+ Image edit
285
+ makaron chat --project <id> --image photo.jpg "remove the person in the background"
286
+
287
+ Image generation
288
+ makaron chat --project auto "generate a cinematic poster of a rainy Tokyo alley"
289
+
290
+ Video from image or timeline
291
+ makaron chat --project <id> "make this into a 5 second cinematic video"
292
+
293
+ Fix one video moment from a screenshot
294
+ makaron chat --project <id> --image screenshot.png "@4 this frame should be Paris; only fix this moment"
295
+
296
+ Video cuts and assembly
297
+ makaron chat --project <id> --video clip.mp4 "cut out the dead air and keep the best 20 seconds"
298
+
299
+ Music
300
+ makaron chat --project <id> "add calm piano background music"
301
+
302
+ Motion design
303
+ makaron chat --project <id> "make an animated Instagram story with this image"
304
+
305
+ After async generation:
306
+ The CLI waits for video/music tasks. If the result has a natural next step, it prints:
307
+ Next steps:
308
+ makaron chat --project <id> "..."
309
+ `);
310
+ }
311
+
156
312
  // ─── SSE Consumer ────────────────────────────────────────────────────────────
157
313
 
158
314
  async function abortRun(baseUrl, headers, runId) {
@@ -399,6 +555,7 @@ async function pollRun(baseUrl, headers, runId, opts = {}) {
399
555
  else if (v.status === 'failed') process.stderr.write(`🎬 Video ${v.taskId}: failed${v.error ? ` — ${v.error}` : ''}\n`);
400
556
  else process.stderr.write(`🎬 Video ${v.taskId}: ${v.status || 'submitted'}\n`);
401
557
  }
558
+ printCompletionActions(data);
402
559
  for (const m of data.result.music || []) {
403
560
  if (m.audioUrl) process.stderr.write(`🎵 Music: ${m.audioUrl}\n`);
404
561
  else process.stderr.write(`🎵 Music ${m.taskId}: ${m.status || 'submitted'}\n`);
@@ -426,6 +583,12 @@ function applyPick(data, field) {
426
583
  case 'design_urls': return (data.output || []).filter(o => o.type === 'design' && o.url).map(o => o.url);
427
584
  case 'first_music_url': return data.output?.find(o => o.type === 'music' && o.url)?.url || null;
428
585
  case 'music_urls': return (data.output || []).filter(o => o.type === 'music' && o.url).map(o => o.url);
586
+ case 'next_steps': return collectCompletionActions(data).map(action => ({
587
+ label: action.label,
588
+ prompt: action.prompt,
589
+ description: action.description,
590
+ source: action.source,
591
+ }));
429
592
  case 'project_url': return data.project_url || data.projectUrl || null;
430
593
  case 'output': return data.output || [];
431
594
  case 'text': return data.output?.find(o => o.type === 'text')?.content || null;
@@ -995,6 +1158,8 @@ function printHelp(topic, subtopic) {
995
1158
  const args = process.argv.slice(2);
996
1159
  const command = args[0];
997
1160
 
1161
+ await maybeNotifyUpdate(command, args);
1162
+
998
1163
  if (!command || command === '--help' || command === '-h' || command === 'help') {
999
1164
  printRootHelp();
1000
1165
  } else if (hasHelpFlag(args)) {
@@ -1019,7 +1184,10 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
1019
1184
  }
1020
1185
  await createProject(baseUrl, headers, opts);
1021
1186
  } else if (command === 'chat') {
1022
- const { headers, baseUrl } = getAuth();
1187
+ if (args.includes('--help') || args.includes('-h')) {
1188
+ printChatHelp();
1189
+ process.exit(0);
1190
+ }
1023
1191
  let projectId = null;
1024
1192
  const chatImages = [];
1025
1193
  const chatVideos = [];
@@ -1027,12 +1195,14 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
1027
1195
  let useStream = false;
1028
1196
  let background = false;
1029
1197
  let jsonOutput = false;
1198
+ let activeSkill = undefined;
1030
1199
  let videoModel = undefined;
1031
1200
  let preferredModel = undefined;
1032
1201
  for (let i = 1; i < args.length; i++) {
1033
1202
  if (args[i] === '--project' && args[i + 1]) projectId = args[++i];
1034
1203
  else if (args[i] === '--image' && args[i + 1]) chatImages.push(args[++i]);
1035
1204
  else if (args[i] === '--video' && args[i + 1]) chatVideos.push(args[++i]);
1205
+ else if (args[i] === '--skill' && args[i + 1]) activeSkill = args[++i];
1036
1206
  else if (args[i] === '--stream') useStream = true;
1037
1207
  else if (args[i] === '--background' || args[i] === '-b') background = true;
1038
1208
  else if (args[i] === '--json') jsonOutput = true;
@@ -1042,9 +1212,11 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
1042
1212
  }
1043
1213
  const prompt = promptParts.join(' ');
1044
1214
  if (!prompt) {
1045
- console.error('Usage: makaron chat --project <id|auto> [--image <file>] [--video <file|url>] [--stream] [--background|-b] [--json] "your message"');
1215
+ console.error('Usage: makaron chat --project <id|auto> [options] "your message"');
1216
+ console.error('Run: makaron chat --help');
1046
1217
  process.exit(1);
1047
1218
  }
1219
+ const { headers, baseUrl } = getAuth();
1048
1220
  // Split images into URLs vs local files
1049
1221
  const imageUrlList = chatImages.filter(p => p.startsWith('http://') || p.startsWith('https://'));
1050
1222
  const imageFileList = chatImages.filter(p => !p.startsWith('http://') && !p.startsWith('https://'));
@@ -1122,7 +1294,7 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
1122
1294
  }
1123
1295
 
1124
1296
  // Upload videos to project timeline (via /api/projects/create with videoUrls)
1125
- let finalPrompt = prompt;
1297
+ let finalPrompt = activeSkill ? `[Active skill: ${activeSkill}]\n${prompt}` : prompt;
1126
1298
  if (chatVideos.length > 0) {
1127
1299
  // Upload local files via signed URL (no size limit, works with API key auth)
1128
1300
  const uploadedVideoUrls = [...prevalidatedVideoUrlList];
@@ -1166,7 +1338,7 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
1166
1338
 
1167
1339
  // Inject hint so Agent knows videos are available
1168
1340
  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}`;
1341
+ finalPrompt = `${finalPrompt}\n\n${hint}`;
1170
1342
  }
1171
1343
 
1172
1344
  if (useStream) {
@@ -1311,7 +1483,7 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
1311
1483
  else promptParts.push(args[i]);
1312
1484
  }
1313
1485
  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); }
1486
+ if (!editArgs.editPrompt) { console.error('Usage: makaron edit [--image <file|url>] [--model gemini|qwen|openai] [--ref <file>] [--out <file>] "prompt"'); process.exit(1); }
1315
1487
  process.stderr.write('🎨 Generating...\n');
1316
1488
  const result = await callMcpTool(baseUrl, headers, 'makaron_edit_image', editArgs);
1317
1489
  saveMcpImage(result, outputPath);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "makaron-cli",
3
- "version": "0.8.4",
3
+ "version": "0.9.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
 
@@ -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" |
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.