gunni 0.3.6 → 0.3.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +95 -45
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -69400,7 +69400,8 @@ async function downloadImage(url2, outputPath, onProgress) {
69400
69400
  const correctExt = await detectImageFormat(outputPath);
69401
69401
  const currentExt = extname(outputPath).toLowerCase();
69402
69402
  if (correctExt && correctExt !== currentExt) {
69403
- const fixedPath = outputPath.slice(0, -currentExt.length) + correctExt;
69403
+ const basePath = currentExt ? outputPath.slice(0, -currentExt.length) : outputPath;
69404
+ const fixedPath = basePath + correctExt;
69404
69405
  await rename(outputPath, fixedPath);
69405
69406
  return fixedPath;
69406
69407
  }
@@ -69431,7 +69432,8 @@ async function saveImageData(data, outputPath, onProgress) {
69431
69432
  const correctExt = await detectImageFormat(outputPath);
69432
69433
  const currentExt = extname(outputPath).toLowerCase();
69433
69434
  if (correctExt && correctExt !== currentExt) {
69434
- const fixedPath = outputPath.slice(0, -currentExt.length) + correctExt;
69435
+ const basePath = currentExt ? outputPath.slice(0, -currentExt.length) : outputPath;
69436
+ const fixedPath = basePath + correctExt;
69435
69437
  await rename(outputPath, fixedPath);
69436
69438
  return fixedPath;
69437
69439
  }
@@ -86261,7 +86263,7 @@ var RemoteClient = class {
86261
86263
  }
86262
86264
  }
86263
86265
  );
86264
- this.client = new Client({ name: "gunni-cli", version: "0.3.6" });
86266
+ this.client = new Client({ name: "gunni-cli", version: "0.3.8" });
86265
86267
  await this.client.connect(transport);
86266
86268
  }
86267
86269
  async callTool(name, args) {
@@ -86435,40 +86437,58 @@ var DEFAULT_MODELS = {
86435
86437
  "remove-bg": "bria-bg-remove"
86436
86438
  };
86437
86439
  function registerImageCommand(program3) {
86438
- program3.command("image").description("Unified image command: generate, edit, describe, upscale, or remove background").argument("[input]", "Image path or text prompt").argument("[prompt]", "Edit instructions (when input is an image)").option("-m, --model <model>", "Model to use (auto-selected by operation if omitted)").option("-o, --output <path>", "Output file path (default: auto-generated)").option("--width <pixels>", "Image width in pixels", parseInt).option("--height <pixels>", "Image height in pixels", parseInt).option("--seed <number>", "Random seed for reproducibility", parseInt).option("--upscale", "Upscale the input image").option("--remove-bg", "Remove background from the input image").option("--scale <factor>", "Scale factor for upscale (default: 2)", parseInt, 2).option("--variants <count>", "Number of variants to generate", parseInt).option("--style <name>", "Apply a saved visual style").addHelpText(
86440
+ program3.command("image").description("Unified image command: generate, edit, describe, upscale, or remove background").argument("[inputs...]", "Image path(s) or text prompt. Multiple images for multi-ref editing.").option("-m, --model <model>", "Model to use (auto-selected by operation if omitted)").option("-o, --output <path>", "Output file path (default: auto-generated)").option("--width <pixels>", "Image width in pixels", parseInt).option("--height <pixels>", "Image height in pixels", parseInt).option("--seed <number>", "Random seed for reproducibility", parseInt).option("--upscale", "Upscale the input image").option("--remove-bg", "Remove background from the input image").option("--scale <factor>", "Scale factor for upscale (default: 2)", parseInt, 2).option("--variants <count>", "Number of variants to generate", parseInt).option("--style <name>", "Apply a saved visual style").addHelpText(
86439
86441
  "after",
86440
86442
  `
86441
86443
  Examples:
86442
86444
 
86443
86445
  Generate an image:
86444
- $ gunni image "a cat on a beach" -o cat.png
86445
- $ gunni image "product photo" --model nano-banana -o product.png
86446
+ $ gunni image "a cat on a beach" -o cat
86447
+ $ gunni image "product photo" --model nano-banana -o product
86446
86448
 
86447
86449
  Edit an image:
86448
- $ gunni image photo.jpg "make the lighting warmer" -o photo-warm.jpg
86449
- $ gunni image logo.png "place on a coffee cup mockup" -o mockup.png
86450
+ $ gunni image photo.jpg "make the lighting warmer" -o photo-warm
86451
+ $ gunni image logo.png "place on a coffee cup mockup" -o mockup
86452
+
86453
+ Multi-image edit (combine references):
86454
+ $ gunni image fox.jpg forest.jpg "place the fox from @image1 on the path in @image2"
86455
+ $ gunni image character.png style-ref.png "redraw @image1 in the style of @image2"
86450
86456
 
86451
86457
  Describe an image:
86452
86458
  $ gunni image photo.jpg
86453
86459
 
86454
- Upscale an image:
86455
- $ gunni image photo.jpg --upscale -o photo-hires.jpg
86456
- $ gunni image photo.jpg --upscale --scale 4 -o photo-4x.jpg
86457
-
86458
- Remove background:
86459
- $ gunni image product.png --remove-bg -o product-clean.png
86460
+ Upscale / remove background:
86461
+ $ gunni image photo.jpg --upscale -o photo-hires
86462
+ $ gunni image product.png --remove-bg -o product-clean
86460
86463
 
86461
86464
  Explore with variants:
86462
- $ gunni image "logo concepts" --variants 4 -o concepts/logo-{n}.png
86465
+ $ gunni image "logo concepts" --variants 4 -o concepts/logo-{n}
86463
86466
 
86464
86467
  Agent usage:
86465
86468
  $ gunni --json image "product photo" --variants 3
86466
-
86467
- Returns: { operation, results: [{ image: { path, url, width, height }, seed }], model, prompt }
86468
86469
  `
86469
- ).action(async (input, prompt, opts) => {
86470
+ ).action(async (inputs, opts) => {
86470
86471
  const json2 = program3.opts().json ?? false;
86471
86472
  try {
86473
+ if (!inputs || inputs.length === 0) {
86474
+ throw new GunniError(
86475
+ "No input provided. Pass an image path or text prompt.",
86476
+ "MISSING_INPUT",
86477
+ "Run `gunni image --help` for usage examples."
86478
+ );
86479
+ }
86480
+ const imagePaths = [];
86481
+ let prompt;
86482
+ for (const arg of inputs) {
86483
+ if (existsSync(arg)) {
86484
+ imagePaths.push(arg);
86485
+ } else if (!prompt) {
86486
+ prompt = arg;
86487
+ } else {
86488
+ prompt += " " + arg;
86489
+ }
86490
+ }
86491
+ const input = imagePaths.length > 0 ? imagePaths[0] : prompt;
86472
86492
  if (!input) {
86473
86493
  throw new GunniError(
86474
86494
  "No input provided. Pass an image path or text prompt.",
@@ -86476,13 +86496,13 @@ Examples:
86476
86496
  "Run `gunni image --help` for usage examples."
86477
86497
  );
86478
86498
  }
86479
- const isFile = existsSync(input);
86499
+ const isFile = imagePaths.length > 0;
86480
86500
  const operation = routeOperation(isFile, prompt, opts);
86481
86501
  const config2 = new ConfigManager();
86482
86502
  const gunniKey = await config2.getGunniApiKey();
86483
86503
  if (gunniKey) {
86484
86504
  const serverUrl = await config2.getServerUrl();
86485
- return handleRemote(input, prompt, operation, opts, json2, gunniKey, serverUrl);
86505
+ return handleRemote(imagePaths.length > 1 ? imagePaths : input, prompt, operation, opts, json2, gunniKey, serverUrl);
86486
86506
  }
86487
86507
  throw new GunniError(
86488
86508
  "No API key configured",
@@ -86553,9 +86573,10 @@ function resolveVariantPath(template, index) {
86553
86573
  return `${base}-${index}${ext}`;
86554
86574
  }
86555
86575
  async function handleRemote(input, prompt, operation, opts, json2, apiKey, serverUrl) {
86576
+ const isMultiImage = Array.isArray(input);
86556
86577
  const spinnerLabels = {
86557
86578
  generate: "Painting pixels\u2026",
86558
- edit: "Reworking the canvas\u2026",
86579
+ edit: isMultiImage ? `Combining ${input.length} images\u2026` : "Reworking the canvas\u2026",
86559
86580
  describe: "Studying the image\u2026",
86560
86581
  upscale: "Enhancing resolution\u2026",
86561
86582
  "remove-bg": "Cutting out the background\u2026"
@@ -86567,8 +86588,17 @@ async function handleRemote(input, prompt, operation, opts, json2, apiKey, serve
86567
86588
  try {
86568
86589
  let imageUrl;
86569
86590
  if (operation !== "generate") {
86570
- update("Uploading image\u2026");
86571
- imageUrl = await uploadLocalFile(input, apiKey, serverUrl);
86591
+ if (isMultiImage) {
86592
+ const urls = [];
86593
+ for (let i2 = 0; i2 < input.length; i2++) {
86594
+ update(`Uploading image ${i2 + 1} of ${input.length}\u2026`);
86595
+ urls.push(await uploadLocalFile(input[i2], apiKey, serverUrl));
86596
+ }
86597
+ imageUrl = urls;
86598
+ } else {
86599
+ update("Uploading image\u2026");
86600
+ imageUrl = await uploadLocalFile(input, apiKey, serverUrl);
86601
+ }
86572
86602
  }
86573
86603
  let effectiveModel = opts.model ?? DEFAULT_MODELS[operation];
86574
86604
  let effectivePrompt = operation === "generate" ? input : prompt;
@@ -87891,7 +87921,7 @@ KEY PRINCIPLES
87891
87921
  }
87892
87922
  },
87893
87923
  latest: {
87894
- version: "0.3.4",
87924
+ version: "0.3.7",
87895
87925
  date: "2026-03-29",
87896
87926
  updates: [
87897
87927
  "Production system: templates (proven prompts with variables), styles (visual direction), presets (platform rules), pipelines (multi-step workflows).",
@@ -87969,9 +87999,27 @@ ${import_picocolors8.default.dim("Run: gunni learn <topic>")}
87969
87999
  `);
87970
88000
  } else {
87971
88001
  console.log(`
87972
- ${import_picocolors8.default.bold(result.title)}
88002
+ ${import_picocolors8.default.bold(import_picocolors8.default.cyan(result.title))}
87973
88003
  `);
87974
- console.log(result.content);
88004
+ const lines = result.content.split("\n");
88005
+ for (const line of lines) {
88006
+ if (/^[A-Z][A-Z _/()]+:/.test(line)) {
88007
+ const colonIdx = line.indexOf(":");
88008
+ console.log(`${import_picocolors8.default.bold(line.slice(0, colonIdx + 1))}${import_picocolors8.default.dim(line.slice(colonIdx + 1))}`);
88009
+ } else if (line.startsWith("- ")) {
88010
+ const dashEnd = line.indexOf(" ", 2);
88011
+ const toolMatch = line.match(/^- (\w+\(\)[^ ]*)(.*)/);
88012
+ if (toolMatch) {
88013
+ console.log(` ${import_picocolors8.default.green("\u2022")} ${import_picocolors8.default.cyan(toolMatch[1])}${import_picocolors8.default.dim(toolMatch[2])}`);
88014
+ } else {
88015
+ console.log(` ${import_picocolors8.default.green("\u2022")} ${line.slice(2)}`);
88016
+ }
88017
+ } else if (/^\d+\./.test(line)) {
88018
+ console.log(` ${import_picocolors8.default.dim(line)}`);
88019
+ } else {
88020
+ console.log(line);
88021
+ }
88022
+ }
87975
88023
  console.log();
87976
88024
  }
87977
88025
  } catch (err) {
@@ -89391,7 +89439,8 @@ Examples:
89391
89439
  console.log(`
89392
89440
  ${import_picocolors14.default.bold(import_picocolors14.default.cyan(category.toUpperCase()))}`);
89393
89441
  for (const m2 of catModels) {
89394
- console.log(` ${import_picocolors14.default.bold(m2.id)} ${import_picocolors14.default.dim(m2.description)}`);
89442
+ const tag = m2.isDefault ? import_picocolors14.default.green(" \u2605") : "";
89443
+ console.log(` ${import_picocolors14.default.bold(m2.id)}${tag} ${import_picocolors14.default.dim(m2.description)}`);
89395
89444
  }
89396
89445
  }
89397
89446
  console.log();
@@ -90896,7 +90945,7 @@ async function runOnboarding() {
90896
90945
 
90897
90946
  // src/index.ts
90898
90947
  var program2 = new Command();
90899
- program2.name("gunni").description("AI media toolkit \u2014 give any agent the ability to create professional media").version("0.3.6").option("--json", "Output results as JSON").addHelpText(
90948
+ program2.name("gunni").description("AI media toolkit \u2014 give any agent the ability to create professional media").version("0.3.8").option("--json", "Output results as JSON").addHelpText(
90900
90949
  "after",
90901
90950
  `
90902
90951
  Examples:
@@ -90906,18 +90955,18 @@ Examples:
90906
90955
  $ gunni init --template brand # Start a brand project
90907
90956
 
90908
90957
  Image (generate, edit, describe, upscale, remove-bg):
90909
- $ gunni image "a coffee bag on white background" -o bag.png
90910
- $ gunni image bag.png --remove-bg -o bag-clean.png
90911
- $ gunni image bag-clean.png "place on marble counter" -o hero.png
90912
- $ gunni image hero.png --upscale -o hero-final.png
90913
- $ gunni image hero-final.png # describe
90958
+ $ gunni image "a coffee bag on white background" -o bag
90959
+ $ gunni image bag.jpg --remove-bg -o bag-clean
90960
+ $ gunni image bag-clean.png "place on marble counter" -o hero
90961
+ $ gunni image hero.jpg --upscale -o hero-final
90962
+ $ gunni image hero-final.jpg # describe
90914
90963
 
90915
90964
  Video & audio:
90916
- $ gunni video hero.png -p "slow cinematic zoom in" -o hero.mp4
90965
+ $ gunni video hero.jpg -p "slow cinematic zoom in" -o hero.mp4
90917
90966
  $ gunni audio "Welcome to our roastery" -o intro.mp3
90918
90967
 
90919
90968
  Explore with variants:
90920
- $ gunni image "logo concepts" --variants 4 -o logo-{n}.png
90969
+ $ gunni image "logo concepts" --variants 4 -o logo-{n}
90921
90970
 
90922
90971
  Creative expertise:
90923
90972
  $ gunni learn exploration # How to explore creatively
@@ -90926,8 +90975,8 @@ Examples:
90926
90975
  Agent workflow (structured JSON):
90927
90976
  $ gunni --json learn overview # Load capabilities
90928
90977
  $ gunni --json image "product photo" --variants 3 # Explore
90929
- $ gunni --json image best.png "refine the lighting" # Iterate
90930
- $ gunni --json image final.png --upscale # Polish
90978
+ $ gunni --json image best.jpg "refine the lighting" # Iterate
90979
+ $ gunni --json image final.jpg --upscale # Polish
90931
90980
  `
90932
90981
  );
90933
90982
  registerImageCommand(program2);
@@ -90948,7 +90997,7 @@ program2.command("models").description("List available models (alias for: gunni
90948
90997
  return;
90949
90998
  }
90950
90999
  if (models.length === 0) {
90951
- console.log(`No models found${opts.type ? ` for category "${opts.type}"` : ""}.`);
91000
+ console.log(import_picocolors23.default.dim(`No models found${opts.type ? ` for category "${opts.type}"` : ""}.`));
90952
91001
  return;
90953
91002
  }
90954
91003
  const grouped = /* @__PURE__ */ new Map();
@@ -90959,9 +91008,10 @@ program2.command("models").description("List available models (alias for: gunni
90959
91008
  }
90960
91009
  for (const [category, catModels] of grouped) {
90961
91010
  console.log(`
90962
- ${category.toUpperCase()}`);
91011
+ ${import_picocolors23.default.bold(import_picocolors23.default.cyan(category.toUpperCase()))}`);
90963
91012
  for (const m2 of catModels) {
90964
- console.log(` ${m2.id} ${m2.description}`);
91013
+ const tag = m2.isDefault ? import_picocolors23.default.green(" \u2605") : "";
91014
+ console.log(` ${import_picocolors23.default.bold(m2.id)}${tag} ${import_picocolors23.default.dim(m2.description)}`);
90965
91015
  }
90966
91016
  }
90967
91017
  console.log();
@@ -90995,7 +91045,7 @@ program2.action(async () => {
90995
91045
  { name: "models", usage: "gunni models [--type <category>]", description: "List available models" },
90996
91046
  { name: "config", usage: "gunni config", description: "Manage API keys" }
90997
91047
  ];
90998
- console.log(JSON.stringify({ version: "0.3.6", commands, categories, models }, null, 2));
91048
+ console.log(JSON.stringify({ version: "0.3.8", commands, categories, models }, null, 2));
90999
91049
  return;
91000
91050
  }
91001
91051
  const configured = await hasGunniKey();
@@ -91051,10 +91101,10 @@ ${import_picocolors23.default.bold("Models:")} ${import_picocolors23.default.dim
91051
91101
  }
91052
91102
  console.log(`
91053
91103
  ${import_picocolors23.default.bold("Quick start:")}
91054
- ${import_picocolors23.default.dim("$")} gunni image "coffee bag product shot" -o bag.png
91055
- ${import_picocolors23.default.dim("$")} gunni image bag.png --remove-bg -o bag-clean.png
91056
- ${import_picocolors23.default.dim("$")} gunni image bag-clean.png "on marble counter" -o hero.png
91057
- ${import_picocolors23.default.dim("$")} gunni image hero.png --upscale -o hero-final.png
91104
+ ${import_picocolors23.default.dim("$")} gunni image "coffee bag product shot" -o bag
91105
+ ${import_picocolors23.default.dim("$")} gunni image bag.jpg --remove-bg -o bag-clean
91106
+ ${import_picocolors23.default.dim("$")} gunni image bag-clean.png "on marble counter" -o hero
91107
+ ${import_picocolors23.default.dim("$")} gunni image hero.jpg --upscale -o hero-final
91058
91108
 
91059
91109
  ${import_picocolors23.default.dim("Add --json to any command for structured output.")}
91060
91110
  ${import_picocolors23.default.dim("Run gunni learn to load creative expertise.")}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gunni",
3
- "version": "0.3.6",
3
+ "version": "0.3.8",
4
4
  "description": "AI media toolkit — give any agent the ability to create professional media",
5
5
  "type": "module",
6
6
  "bin": {