makaron-cli 0.10.0 → 0.11.1

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,7 +1,7 @@
1
1
  {
2
2
  "name": "makaron-cli",
3
- "version": "0.9.0",
4
- "description": "AI image editing, video generation, and music creation via CLI. Agents can self-register, create projects, and produce creative media.",
3
+ "version": "0.11.1",
4
+ "description": "AI image editing, video generation, music creation, and marketplace skill workflows via CLI. Agents can self-register, install skills, create projects, and produce creative media.",
5
5
  "author": {
6
6
  "name": "Makaron AI",
7
7
  "url": "https://www.makaron.app"
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "makaron-cli",
3
- "version": "0.9.0",
4
- "description": "AI image editing, video generation, and music creation via CLI. Agents can self-register, create projects, and produce creative media.",
3
+ "version": "0.11.1",
4
+ "description": "AI image editing, video generation, music creation, and marketplace skill workflows via CLI. Agents can self-register, install skills, create projects, and produce creative media.",
5
5
  "displayName": "Makaron",
6
6
  "shortDescription": "AI image/video/music creation from the terminal",
7
- "longDescription": "makaron.app is for humans. makaron-cli is for AI agents. Talk to Makaron Agent from the terminal — create projects, edit images, generate videos, and compose music. Zero dependencies, single file, works with npx. Agents can self-register to get an API key without human intervention.",
7
+ "longDescription": "makaron.app is for humans. makaron-cli is for AI agents. Talk to Makaron Agent from the terminal — list, search, show, and install marketplace skills; create projects; edit images; generate videos; and compose music. Zero dependencies, single file, works with npx. Agents can self-register to get an API key without human intervention.",
8
8
  "author": {
9
9
  "name": "Makaron AI",
10
10
  "url": "https://www.makaron.app"
@@ -12,9 +12,9 @@
12
12
  "homepage": "https://www.makaron.app/agent",
13
13
  "repository": "https://github.com/vegekyd/ai-image-editor",
14
14
  "license": "MIT",
15
- "keywords": ["ai", "image-editing", "video", "music", "agent", "creative"],
15
+ "keywords": ["ai", "image-editing", "video", "music", "agent", "creative", "skills"],
16
16
  "category": "Creative Tools",
17
- "capabilities": ["image-editing", "video-generation", "music-creation", "text-to-image"],
17
+ "capabilities": ["image-editing", "video-generation", "music-creation", "text-to-image", "marketplace-skills"],
18
18
  "skills": "./skills/",
19
19
  "websiteURL": "https://www.makaron.app",
20
20
  "brandColor": "#d946ef"
package/README.md CHANGED
@@ -7,8 +7,10 @@ Makaron is a multimodal AI creative agent. You talk to it via `makaron chat`, an
7
7
  ## Setup
8
8
 
9
9
  ```bash
10
- npm install -g makaron-cli
11
- # or use directly: npx makaron-cli
10
+ # Install makaron-cli globally and add the Makaron Agent Skill.
11
+ npx makaron-cli setup
12
+
13
+ npx makaron-cli --help
12
14
  ```
13
15
 
14
16
  ### Get your API key
@@ -107,6 +109,23 @@ Returns immediately:
107
109
  | Add music | `npx makaron-cli chat --project <id> "add calm piano background music"` |
108
110
  | Create motion design | `npx makaron-cli chat --project <id> "make an animated Instagram story with this image"` |
109
111
 
112
+ ### Marketplace skills
113
+
114
+ External users can browse, install, and use marketplace skills with only `MAKARON_API_KEY`:
115
+
116
+ ```bash
117
+ npx makaron-cli skills list
118
+ npx makaron-cli skills search "football"
119
+ npx makaron-cli skills search "足球"
120
+ npx makaron-cli skills show <marketplace-id-or-label>
121
+ npx makaron-cli skills install <marketplace-id-or-label>
122
+
123
+ # chat auto-installs matched marketplace skills before starting the run
124
+ npx makaron-cli chat --project auto --image selfie.jpg --skill <marketplace-id-or-label> -b "make this with the selected skill"
125
+ ```
126
+
127
+ `--skill` accepts an installed skill name, a marketplace UUID, or a unique marketplace label. If a marketplace skill is matched, the CLI installs or reuses it and sends `[Active skill: <installed-skill-name>]` to Makaron Agent. Ordinary users do not need admin commands, and the CLI intentionally does not expose skill deletion.
128
+
110
129
  ### With additional images (existing project)
111
130
 
112
131
  ```bash
package/bin/makaron.mjs CHANGED
@@ -15,6 +15,7 @@ import fs from 'fs';
15
15
  import path from 'path';
16
16
  import { createInterface } from 'readline';
17
17
  import { execFileSync } from 'child_process';
18
+ import { fileURLToPath } from 'url';
18
19
 
19
20
  // ─── Config ──────────────────────────────────────────────────────────────────
20
21
 
@@ -267,12 +268,13 @@ function printChatHelp() {
267
268
  console.log(`Makaron chat — create and edit with Makaron Agent
268
269
 
269
270
  Usage:
270
- makaron chat --project <id|auto> [options] "your message"
271
+ makaron chat --project <id|auto> [options] [--skill <id|label|name>] "your message"
271
272
 
272
273
  Options:
273
274
  --project <id|auto> Project to work in. Use "auto" to create one.
274
275
  --image <file|url> Attach a reference image or screenshot. Repeatable.
275
276
  --video <file|url> Attach a video to the project timeline. Repeatable.
277
+ --skill <id|label|name> Use an installed skill or auto-install a matched marketplace skill.
276
278
  --model <name> Preferred image/model route.
277
279
  --video-model <name> Preferred video model: seedance-fast, seedance, kling, or grok.
278
280
  --video-resolution <res> Video resolution: auto, 480p, 720p, 1080p, or 4k.
@@ -291,6 +293,9 @@ What you can ask:
291
293
  Video from image or timeline
292
294
  makaron chat --project <id> "make this into a 5 second cinematic video"
293
295
 
296
+ Marketplace skill
297
+ makaron chat --project auto --image selfie.jpg --skill "Football Captain" "make this cinematic"
298
+
294
299
  Fix one video moment from a screenshot
295
300
  makaron chat --project <id> --image screenshot.png "@4 this frame should be Paris; only fix this moment"
296
301
 
@@ -792,6 +797,155 @@ function timeSince(date) {
792
797
  return `${Math.floor(s / 86400)}d ago`;
793
798
  }
794
799
 
800
+ // ─── Skill Marketplace ──────────────────────────────────────────────────────
801
+
802
+ function getSkillLabel(skill) {
803
+ return skill.labels?.en || skill.labels?.zh || skill.label || skill.name || skill.id;
804
+ }
805
+
806
+ function slugifySkill(value) {
807
+ return String(value || '')
808
+ .toLowerCase()
809
+ .normalize('NFKD')
810
+ .replace(/[\u0300-\u036f]/g, '')
811
+ .replace(/[^a-z0-9]+/g, '-')
812
+ .replace(/^-+|-+$/g, '');
813
+ }
814
+
815
+ function normalizeMarketplaceSkill(skill) {
816
+ return {
817
+ ...skill,
818
+ label: getSkillLabel(skill),
819
+ skillPath: skill.skillPath || skill.skill_path || null,
820
+ hasSkill: Boolean(skill.skillPath || skill.skill_path),
821
+ };
822
+ }
823
+
824
+ async function fetchMarketplaceSkills(baseUrl, opts = {}) {
825
+ let res;
826
+ try {
827
+ res = await fetch(`${baseUrl}/api/home-skills`);
828
+ } catch (err) {
829
+ if (opts.optional) return null;
830
+ process.stderr.write(`Failed to load marketplace skills: ${err.message || err}\n`);
831
+ process.exit(1);
832
+ }
833
+ if (!res.ok) {
834
+ if (opts.optional) return null;
835
+ process.stderr.write(`Error ${res.status}: ${await res.text()}\n`);
836
+ process.exit(1);
837
+ }
838
+ const data = await res.json();
839
+ const skills = Array.isArray(data) ? data : (data.skills || []);
840
+ return skills.map(normalizeMarketplaceSkill);
841
+ }
842
+
843
+ function marketplaceSearchText(skill) {
844
+ return [
845
+ skill.id,
846
+ skill.label,
847
+ skill.labels?.en,
848
+ skill.labels?.zh,
849
+ skill.prompt,
850
+ slugifySkill(skill.label),
851
+ slugifySkill(skill.labels?.en),
852
+ slugifySkill(skill.labels?.zh),
853
+ ].filter(Boolean).join(' ').toLowerCase();
854
+ }
855
+
856
+ function marketplaceSkillTokens(skill) {
857
+ return [
858
+ skill.id,
859
+ skill.label,
860
+ skill.labels?.en,
861
+ skill.labels?.zh,
862
+ skill.prompt,
863
+ ].filter(Boolean).map(value => String(value).toLowerCase());
864
+ }
865
+
866
+ function findMarketplaceSkill(skills, identifier) {
867
+ const raw = String(identifier || '').trim();
868
+ if (!raw) return null;
869
+ const lower = raw.toLowerCase();
870
+ const slug = slugifySkill(raw);
871
+ const exact = skills.filter(skill => {
872
+ const labels = Object.values(skill.labels || {}).map(v => String(v).toLowerCase());
873
+ const slugMatches = slug
874
+ ? slugifySkill(getSkillLabel(skill)) === slug ||
875
+ Object.values(skill.labels || {}).some(v => slugifySkill(v) === slug)
876
+ : false;
877
+ return skill.id === raw ||
878
+ skill.id?.toLowerCase() === lower ||
879
+ getSkillLabel(skill).toLowerCase() === lower ||
880
+ labels.includes(lower) ||
881
+ slugMatches;
882
+ });
883
+ if (exact.length === 1) return exact[0];
884
+ if (exact.length > 1) {
885
+ process.stderr.write(`Multiple marketplace skills match "${raw}". Use an id:\n`);
886
+ exact.forEach(skill => process.stderr.write(` ${skill.id} ${skill.label}\n`));
887
+ process.exit(1);
888
+ }
889
+ const prefix = skills.filter(skill => skill.id?.startsWith(raw));
890
+ if (prefix.length === 1) return prefix[0];
891
+ return null;
892
+ }
893
+
894
+ function printMarketplaceSkills(skills) {
895
+ if (!skills.length) {
896
+ console.log('No marketplace skills found.');
897
+ return;
898
+ }
899
+ console.log(`📦 ${skills.length} marketplace skills\n`);
900
+ for (const skill of skills) {
901
+ const kind = skill.hasSkill ? 'skill' : 'prompt';
902
+ console.log(` ${skill.id} ${skill.label} [${kind}]`);
903
+ }
904
+ }
905
+
906
+ function printMarketplaceSkill(skill) {
907
+ console.log(`${skill.label}`);
908
+ console.log(`ID: ${skill.id}`);
909
+ console.log(`Type: ${skill.hasSkill ? 'installable skill' : 'prompt template'}`);
910
+ if (skill.labels?.zh && skill.labels.zh !== skill.label) console.log(`ZH: ${skill.labels.zh}`);
911
+ if (skill.prompt) console.log(`Prompt: ${skill.prompt}`);
912
+ if (skill.image) console.log(`Cover: ${skill.image}`);
913
+ if (skill.hasSkill) console.log(`Install: makaron skills install ${skill.id}`);
914
+ }
915
+
916
+ async function installMarketplaceSkill(baseUrl, headers, skill, opts = {}) {
917
+ if (!skill.hasSkill) {
918
+ process.stderr.write(`"${skill.label}" is a prompt-only marketplace item and has no installable skill package.\n`);
919
+ process.exit(1);
920
+ }
921
+ if (!opts.quiet) process.stderr.write(`📦 Installing skill: ${skill.label}\n`);
922
+ const res = await fetch(`${baseUrl}/api/skills`, {
923
+ method: 'POST',
924
+ headers: { 'Content-Type': 'application/json', ...headers },
925
+ body: JSON.stringify({ skillPath: skill.skillPath, homeSkillId: skill.id }),
926
+ });
927
+ const data = await res.json().catch(() => ({}));
928
+ if (!res.ok || !data.success) {
929
+ process.stderr.write(`Install failed: ${data.error || res.status}\n`);
930
+ process.exit(1);
931
+ }
932
+ if (!opts.quiet) {
933
+ const suffix = data.alreadyInstalled ? 'already installed' : 'installed';
934
+ process.stderr.write(`✅ Skill ${suffix}: ${data.skillName}\n`);
935
+ }
936
+ return data;
937
+ }
938
+
939
+ async function resolveChatSkill(baseUrl, headers, activeSkill) {
940
+ if (!activeSkill) return undefined;
941
+ const skills = await fetchMarketplaceSkills(baseUrl, { optional: true });
942
+ if (!skills) return activeSkill;
943
+ const marketplaceSkill = findMarketplaceSkill(skills, activeSkill);
944
+ if (!marketplaceSkill) return activeSkill;
945
+ const result = await installMarketplaceSkill(baseUrl, headers, marketplaceSkill);
946
+ return result.skillName;
947
+ }
948
+
795
949
  // ─── MCP Tool Caller ─────────────────────────────────────────────────────────
796
950
 
797
951
  async function callMcpTool(baseUrl, headers, toolName, args) {
@@ -1048,6 +1202,8 @@ function printRootHelp() {
1048
1202
  console.log(`Makaron CLI — Talk to Makaron Agent from the terminal
1049
1203
 
1050
1204
  Commands:
1205
+ setup Install makaron-cli globally and add the Agent Skill
1206
+ install-skill Install Makaron Agent Skill into your coding agent
1051
1207
  register --json Get challenge for agent self-registration
1052
1208
  register --verify --challenge-id <id> --answer <n> Verify and save API key
1053
1209
  claim Get claim URL for human to link account
@@ -1059,6 +1215,7 @@ Commands:
1059
1215
  create --title "name" Create empty project (text-to-image)
1060
1216
 
1061
1217
  chat --project <id> "message" Chat (non-blocking, polls for result)
1218
+ chat --project <id> --skill <id> Use or auto-install a marketplace skill
1062
1219
  chat --project <id> --video <file> Attach video to conversation
1063
1220
  chat --project <id> -b "message" Background: submit and print runId
1064
1221
  chat --project <id> --stream "msg" Legacy: stream SSE in real-time
@@ -1068,6 +1225,7 @@ Commands:
1068
1225
  responses get <runId> --wait Poll until completed
1069
1226
  responses list --project <id> List runs for a project
1070
1227
  abort <runId> Abort a running Agent
1228
+ skills list|search|show|install Browse and install marketplace skills
1071
1229
 
1072
1230
  edit [--image <file>] "prompt" AI image edit / text-to-image
1073
1231
  analyze --video <file|url> Analyze video content
@@ -1082,13 +1240,56 @@ Environment:
1082
1240
  `);
1083
1241
  }
1084
1242
 
1243
+ function installAgentSkill(values = []) {
1244
+ if (hasHelpFlag(values)) {
1245
+ console.log('Usage: makaron install-skill [--global] [--agent <agent>] [--yes]');
1246
+ return;
1247
+ }
1248
+
1249
+ const skillDir = fileURLToPath(new URL('../skills/makaron', import.meta.url));
1250
+ const skillFile = path.join(skillDir, 'SKILL.md');
1251
+ if (!fs.existsSync(skillFile)) {
1252
+ console.error(`Makaron Agent Skill not found at ${skillFile}`);
1253
+ process.exit(1);
1254
+ }
1255
+
1256
+ execFileSync('npx', [
1257
+ '-y',
1258
+ 'skills',
1259
+ 'add',
1260
+ skillDir,
1261
+ '--skill',
1262
+ 'makaron',
1263
+ '--copy',
1264
+ ...values,
1265
+ ], { stdio: 'inherit' });
1266
+ }
1267
+
1268
+ function setupMakaron(values = []) {
1269
+ if (hasHelpFlag(values)) {
1270
+ console.log('Usage: makaron setup [--agent <agent>]');
1271
+ return;
1272
+ }
1273
+
1274
+ const version = getCliVersion();
1275
+ console.error(`Installing ${NPM_PACKAGE_NAME}@${version} globally...`);
1276
+ execFileSync('npm', ['install', '-g', `${NPM_PACKAGE_NAME}@${version}`], { stdio: 'inherit' });
1277
+
1278
+ const skillArgs = [...values];
1279
+ if (!skillArgs.includes('--global') && !skillArgs.includes('-g')) skillArgs.unshift('--global');
1280
+ if (!skillArgs.includes('--yes') && !skillArgs.includes('-y')) skillArgs.push('--yes');
1281
+
1282
+ console.error('Installing Makaron Agent Skill globally...');
1283
+ installAgentSkill(skillArgs);
1284
+ }
1285
+
1085
1286
  function printHelp(topic, subtopic) {
1086
1287
  if (topic === 'login') {
1087
1288
  console.log('Usage: makaron login');
1088
1289
  } else if (topic === 'create') {
1089
1290
  console.log('Usage: makaron create --image <file> [--image <file2>] | --image-url <url> | --title "name"');
1090
1291
  } else if (topic === 'chat') {
1091
- console.log('Usage: makaron chat --project <id|auto> [--image <file>] [--video <file|url>] [--stream] [--background|-b] [--json] "your message"');
1292
+ printChatHelp();
1092
1293
  } else if (topic === 'responses' || topic === 'run') {
1093
1294
  if (subtopic === 'get') console.log('Usage: makaron responses get <runId> [--wait] [--json] [--pick <field>]');
1094
1295
  else if (subtopic === 'watch') console.log('Usage: makaron responses watch <runId> [--jsonl] [--interval <ms>]');
@@ -1109,6 +1310,24 @@ function printHelp(topic, subtopic) {
1109
1310
  `);
1110
1311
  } else if (topic === 'abort') {
1111
1312
  console.log('Usage: makaron abort <runId>');
1313
+ } else if (topic === 'setup') {
1314
+ console.log('Usage: makaron setup [--agent <agent>]');
1315
+ } else if (topic === 'install-skill') {
1316
+ console.log('Usage: makaron install-skill [--global] [--agent <agent>] [--yes]');
1317
+ } else if (topic === 'skills') {
1318
+ if (subtopic === 'list') console.log('Usage: makaron skills list [--json]');
1319
+ else if (subtopic === 'search') console.log('Usage: makaron skills search <query> [--json]');
1320
+ else if (subtopic === 'show') console.log('Usage: makaron skills show <marketplace-id|label> [--json]');
1321
+ else if (subtopic === 'install') console.log('Usage: makaron skills install <marketplace-id|label> [--json]');
1322
+ else console.log(`Skill marketplace commands:
1323
+ skills list List marketplace skills
1324
+ skills search <query> Search marketplace skills
1325
+ skills show <id|label> Show a marketplace skill
1326
+ skills install <id|label> Install a marketplace skill to your workspace
1327
+
1328
+ Use with chat:
1329
+ makaron chat --project auto --skill <id|label> "your request"
1330
+ `);
1112
1331
  } else if (topic === 'edit') {
1113
1332
  console.log('Usage: makaron edit [--image <file|url>] [--model gemini|qwen|openai] [--skill enhance|creative|wild|captions] [--ref <file>] [--out <file>] "prompt"');
1114
1333
  } else if (topic === 'analyze') {
@@ -1168,6 +1387,10 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
1168
1387
  printHelp(command, args[1]);
1169
1388
  } else if (command === '--version' || command === '-v' || command === 'version') {
1170
1389
  console.log(getCliVersion());
1390
+ } else if (command === 'setup') {
1391
+ setupMakaron(args.slice(1));
1392
+ } else if (command === 'install-skill') {
1393
+ installAgentSkill(args.slice(1));
1171
1394
  } else if (command === 'login') {
1172
1395
  await login();
1173
1396
  } else if (command === 'create') {
@@ -1206,6 +1429,7 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
1206
1429
  else if (args[i] === '--image' && args[i + 1]) chatImages.push(args[++i]);
1207
1430
  else if (args[i] === '--video' && args[i + 1]) chatVideos.push(args[++i]);
1208
1431
  else if (args[i] === '--skill' && args[i + 1]) activeSkill = args[++i];
1432
+ else if (args[i].startsWith('--skill=')) activeSkill = args[i].slice('--skill='.length);
1209
1433
  else if (args[i] === '--stream') useStream = true;
1210
1434
  else if (args[i] === '--background' || args[i] === '-b') background = true;
1211
1435
  else if (args[i] === '--json') jsonOutput = true;
@@ -1297,8 +1521,10 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
1297
1521
  }
1298
1522
  }
1299
1523
 
1524
+ const resolvedSkill = await resolveChatSkill(baseUrl, headers, activeSkill);
1525
+
1300
1526
  // Upload videos to project timeline (via /api/projects/create with videoUrls)
1301
- let finalPrompt = activeSkill ? `[Active skill: ${activeSkill}]\n${prompt}` : prompt;
1527
+ let finalPrompt = resolvedSkill ? `[Active skill: ${resolvedSkill}]\n${prompt}` : prompt;
1302
1528
  if (chatVideos.length > 0) {
1303
1529
  // Upload local files via signed URL (no size limit, works with API key auth)
1304
1530
  const uploadedVideoUrls = [...prevalidatedVideoUrlList];
@@ -1376,10 +1602,9 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
1376
1602
  if (sub === 'get') {
1377
1603
  const runId = args[2];
1378
1604
  if (!runId) { console.error('Usage: makaron responses get <runId> [--wait] [--json] [--pick <field>]'); process.exit(1); }
1379
- let wait = false, jsonOutput = false, pick = null;
1605
+ let wait = false, pick = null;
1380
1606
  for (let i = 3; i < args.length; i++) {
1381
1607
  if (args[i] === '--wait') wait = true;
1382
- if (args[i] === '--json') jsonOutput = true;
1383
1608
  if (args[i] === '--pick' && args[i + 1]) pick = args[++i];
1384
1609
  }
1385
1610
 
@@ -1439,6 +1664,54 @@ if (!command || command === '--help' || command === '-h' || command === 'help')
1439
1664
  } else if (command === 'list' || command === 'ls') {
1440
1665
  const { headers, baseUrl } = getAuth();
1441
1666
  await listProjects(baseUrl, headers);
1667
+ } else if (command === 'skills') {
1668
+ const sub = args[1] || 'list';
1669
+ const baseUrl = process.env.MAKARON_URL || DEFAULT_URL;
1670
+ const jsonOutput = args.includes('--json');
1671
+
1672
+ if (sub === 'list') {
1673
+ const skills = await fetchMarketplaceSkills(baseUrl);
1674
+ if (jsonOutput) console.log(JSON.stringify({ skills }, null, 2));
1675
+ else printMarketplaceSkills(skills);
1676
+ } else if (sub === 'search') {
1677
+ const query = args.filter((arg, index) => index > 1 && arg !== '--json').join(' ').trim();
1678
+ if (!query) { console.error('Usage: makaron skills search <query> [--json]'); process.exit(1); }
1679
+ const lowerQuery = query.toLowerCase();
1680
+ const slugQuery = slugifySkill(query);
1681
+ const skills = (await fetchMarketplaceSkills(baseUrl))
1682
+ .filter(skill => {
1683
+ const rawMatch = marketplaceSkillTokens(skill).some(token => token.includes(lowerQuery));
1684
+ const slugMatch = slugQuery ? marketplaceSearchText(skill).includes(slugQuery) : false;
1685
+ return rawMatch || slugMatch;
1686
+ });
1687
+ if (jsonOutput) console.log(JSON.stringify({ skills }, null, 2));
1688
+ else printMarketplaceSkills(skills);
1689
+ } else if (sub === 'show') {
1690
+ const identifier = args[2];
1691
+ if (!identifier) { console.error('Usage: makaron skills show <marketplace-id|label> [--json]'); process.exit(1); }
1692
+ const skills = await fetchMarketplaceSkills(baseUrl);
1693
+ const skill = findMarketplaceSkill(skills, identifier);
1694
+ if (!skill) { console.error(`Skill not found: ${identifier}`); process.exit(1); }
1695
+ if (jsonOutput) console.log(JSON.stringify(skill, null, 2));
1696
+ else printMarketplaceSkill(skill);
1697
+ } else if (sub === 'install') {
1698
+ const identifier = args[2];
1699
+ if (!identifier) { console.error('Usage: makaron skills install <marketplace-id|label> [--json]'); process.exit(1); }
1700
+ const { headers, baseUrl: authedBaseUrl } = getAuth();
1701
+ const skills = await fetchMarketplaceSkills(authedBaseUrl);
1702
+ const skill = findMarketplaceSkill(skills, identifier);
1703
+ if (!skill) { console.error(`Skill not found: ${identifier}`); process.exit(1); }
1704
+ const data = await installMarketplaceSkill(authedBaseUrl, headers, skill, { quiet: jsonOutput });
1705
+ if (jsonOutput) console.log(JSON.stringify({ ...data, marketplaceId: skill.id, label: skill.label }, null, 2));
1706
+ else console.log(data.skillName);
1707
+ } else {
1708
+ console.log(`Skill marketplace commands:
1709
+ skills list List marketplace skills
1710
+ skills search <query> Search marketplace skills
1711
+ skills show <id|label> Show a marketplace skill
1712
+ skills install <id|label> Install a marketplace skill to your workspace
1713
+ `);
1714
+ }
1442
1715
  } else if (command === 'project' || command === 'projects') {
1443
1716
  const { headers, baseUrl } = getAuth();
1444
1717
  const sub = args[1];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "makaron-cli",
3
- "version": "0.10.0",
3
+ "version": "0.11.1",
4
4
  "description": "Talk to Makaron Agent from the terminal — create projects, edit images, generate videos",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -96,6 +96,32 @@ Returns immediately:
96
96
  | Add music | `npx makaron-cli chat --project <id> "add calm piano background music"` |
97
97
  | Create motion design | `npx makaron-cli chat --project <id> "make an animated Instagram story with this image"` |
98
98
 
99
+ ### Marketplace skills
100
+
101
+ Use marketplace skills when the user asks for a named Makaron effect, template, or skill such as "Football Captain", "足球队长", "World Cup MVP", or a marketplace UUID.
102
+
103
+ External users only need `MAKARON_API_KEY`; no admin permissions are required for listing, searching, showing, installing, or using marketplace skills.
104
+
105
+ ```bash
106
+ # Browse public marketplace skills
107
+ npx makaron-cli skills list
108
+ npx makaron-cli skills search "football"
109
+ npx makaron-cli skills search "足球"
110
+ npx makaron-cli skills show <marketplace-id-or-label>
111
+
112
+ # Install a marketplace skill into the API key owner's workspace
113
+ npx makaron-cli skills install <marketplace-id-or-label>
114
+
115
+ # Use a marketplace skill. If the skill is not installed yet, chat auto-installs it,
116
+ # then injects the installed skill name into the agent run.
117
+ npx makaron-cli chat --project auto \
118
+ --image selfie.jpg \
119
+ --skill <marketplace-id-or-label> \
120
+ -b "make this with the selected skill"
121
+ ```
122
+
123
+ `--skill` accepts an installed skill name, a marketplace UUID, or a unique marketplace label. If a marketplace skill is matched, the CLI installs or reuses it and sends `[Active skill: <installed-skill-name>]` to Makaron Agent. Do not call admin skill commands for ordinary users. There is intentionally no user-facing CLI delete command for marketplace skills.
124
+
99
125
  ### With additional images (existing project)
100
126
 
101
127
  ```bash