@docyrus/animaguru 0.16.0 → 0.16.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.
package/README.md CHANGED
@@ -55,6 +55,8 @@ Scaffolding requires no AnimaGuru sign-in and spends no generation credits; depe
55
55
 
56
56
  The official PixiJS skills are vendored with their MIT license and a hashed provenance manifest. Skills and template files ship in both the npm package and macOS installer; `create` does not fetch agent instructions from a remote repository at runtime.
57
57
 
58
+ CLI 0.16.1 also installs **AnimaGuru Cutcraft** in both agent skill folders. Ask your agent to use `$animaguru-cutcraft` for tactile paper-collage explainers with exact editorial lettering, one narrator and purposeful cutout motion. Its generation adapter checks the server's enabled models, tracks asynchronous jobs and supports controlled animation of generated transparent artwork when precise placement is needed or video providers are unavailable. Cutcraft is an additional skill; the gallery retains its 31 visual styles.
59
+
58
60
  ## Install the project skill
59
61
 
60
62
  Run this in the folder where you want your coding agent to use AnimaGuru:
package/dist/animaguru.js CHANGED
@@ -10605,12 +10605,12 @@ If you're a developer and you believe this is a bug in isomorphic-git, please fi
10605
10605
  * @param {string} transport
10606
10606
  * @param {string} [suggestion]
10607
10607
  */
10608
- constructor(url2, transport, suggestion) {
10608
+ constructor(url3, transport, suggestion) {
10609
10609
  super(
10610
- `Git remote "${url2}" uses an unrecognized transport protocol: "${transport}"`
10610
+ `Git remote "${url3}" uses an unrecognized transport protocol: "${transport}"`
10611
10611
  );
10612
10612
  this.code = this.name = _UnknownTransportError.code;
10613
- this.data = { url: url2, transport, suggestion };
10613
+ this.data = { url: url3, transport, suggestion };
10614
10614
  }
10615
10615
  };
10616
10616
  UnknownTransportError.code = "UnknownTransportError";
@@ -10618,10 +10618,10 @@ If you're a developer and you believe this is a bug in isomorphic-git, please fi
10618
10618
  /**
10619
10619
  * @param {string} url
10620
10620
  */
10621
- constructor(url2) {
10622
- super(`Cannot parse remote URL: "${url2}"`);
10621
+ constructor(url3) {
10622
+ super(`Cannot parse remote URL: "${url3}"`);
10623
10623
  this.code = this.name = _UrlParseError.code;
10624
- this.data = { url: url2 };
10624
+ this.data = { url: url3 };
10625
10625
  }
10626
10626
  };
10627
10627
  UrlParseError.code = "UrlParseError";
@@ -12487,7 +12487,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
12487
12487
  throw err2;
12488
12488
  }
12489
12489
  }
12490
- async function _addRemote({ fs: fs2, gitdir, remote, url: url2, force }) {
12490
+ async function _addRemote({ fs: fs2, gitdir, remote, url: url3, force }) {
12491
12491
  if (!isValidRef(remote, true)) {
12492
12492
  throw new InvalidRefNameError(remote, cleanGitRef.clean(remote));
12493
12493
  }
@@ -12495,12 +12495,12 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
12495
12495
  if (!force) {
12496
12496
  const remoteNames = await config2.getSubsections("remote");
12497
12497
  if (remoteNames.includes(remote)) {
12498
- if (url2 !== await config2.get(`remote.${remote}.url`)) {
12498
+ if (url3 !== await config2.get(`remote.${remote}.url`)) {
12499
12499
  throw new AlreadyExistsError("remote", remote);
12500
12500
  }
12501
12501
  }
12502
12502
  }
12503
- await config2.set(`remote.${remote}.url`, url2);
12503
+ await config2.set(`remote.${remote}.url`, url3);
12504
12504
  await config2.set(
12505
12505
  `remote.${remote}.fetch`,
12506
12506
  `+refs/heads/*:refs/remotes/${remote}/*`
@@ -12512,21 +12512,21 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
12512
12512
  dir,
12513
12513
  gitdir = join15(dir, ".git"),
12514
12514
  remote,
12515
- url: url2,
12515
+ url: url3,
12516
12516
  force = false
12517
12517
  }) {
12518
12518
  try {
12519
12519
  assertParameter("fs", fs2);
12520
12520
  assertParameter("gitdir", gitdir);
12521
12521
  assertParameter("remote", remote);
12522
- assertParameter("url", url2);
12522
+ assertParameter("url", url3);
12523
12523
  const fsp = new FileSystem(fs2);
12524
12524
  const updatedGitdir = await discoverGitdir({ fsp, dotgit: gitdir });
12525
12525
  return await _addRemote({
12526
12526
  fs: fsp,
12527
12527
  gitdir: updatedGitdir,
12528
12528
  remote,
12529
- url: url2,
12529
+ url: url3,
12530
12530
  force
12531
12531
  });
12532
12532
  } catch (err2) {
@@ -14086,10 +14086,10 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
14086
14086
  if (!ref.startsWith("refs/")) return;
14087
14087
  return fullname ? ref : abbreviateRef(ref);
14088
14088
  }
14089
- function translateSSHtoHTTP(url2) {
14090
- url2 = url2.replace(/^[^/@:]+@([^/@:]+):/, "https://$1/");
14091
- url2 = url2.replace(/^ssh:\/\//, "https://");
14092
- return url2;
14089
+ function translateSSHtoHTTP(url3) {
14090
+ url3 = url3.replace(/^[^/@:]+@([^/@:]+):/, "https://$1/");
14091
+ url3 = url3.replace(/^ssh:\/\//, "https://");
14092
+ return url3;
14093
14093
  }
14094
14094
  function calculateBasicAuthHeader({ username = "", password = "" }) {
14095
14095
  return `Basic ${Buffer.from(`${username}:${password}`).toString("base64")}`;
@@ -14119,16 +14119,16 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
14119
14119
  return result;
14120
14120
  }
14121
14121
  var CREDENTIALS = /^(https?:\/\/)([^/?#]+)@/i;
14122
- function extractAuthFromUrl(url2) {
14123
- const credentials = url2.match(CREDENTIALS);
14124
- if (credentials == null) return { url: url2, auth: {} };
14122
+ function extractAuthFromUrl(url3) {
14123
+ const credentials = url3.match(CREDENTIALS);
14124
+ if (credentials == null) return { url: url3, auth: {} };
14125
14125
  return {
14126
- url: url2.replace(CREDENTIALS, "$1"),
14127
- auth: parseUserinfo(url2, credentials[2])
14126
+ url: url3.replace(CREDENTIALS, "$1"),
14127
+ auth: parseUserinfo(url3, credentials[2])
14128
14128
  };
14129
14129
  }
14130
- function parseUserinfo(url2, userpass) {
14131
- const parsed = parseUrl(url2);
14130
+ function parseUserinfo(url3, userpass) {
14131
+ const parsed = parseUrl(url3);
14132
14132
  const hasPassword = userpass.indexOf(":") !== -1;
14133
14133
  if (parsed !== null) {
14134
14134
  return {
@@ -14142,10 +14142,10 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
14142
14142
  password: hasPassword ? userpass.slice(separatorIndex + 1) : void 0
14143
14143
  };
14144
14144
  }
14145
- function parseUrl(url2) {
14145
+ function parseUrl(url3) {
14146
14146
  if (typeof URL !== "function") return null;
14147
14147
  try {
14148
- return new URL(url2);
14148
+ return new URL(url3);
14149
14149
  } catch (_) {
14150
14150
  return null;
14151
14151
  }
@@ -14269,7 +14269,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
14269
14269
  }
14270
14270
  return split;
14271
14271
  }
14272
- var corsProxify = (corsProxy, url2) => corsProxy.endsWith("?") ? `${corsProxy}${url2}` : `${corsProxy}/${url2.replace(/^https?:\/\//, "")}`;
14272
+ var corsProxify = (corsProxy, url3) => corsProxy.endsWith("?") ? `${corsProxy}${url3}` : `${corsProxy}/${url3.replace(/^https?:\/\//, "")}`;
14273
14273
  var updateHeaders = (headers, auth) => {
14274
14274
  if (auth.username || auth.password) {
14275
14275
  headers.Authorization = calculateBasicAuthHeader(auth);
@@ -14330,8 +14330,8 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
14330
14330
  signal,
14331
14331
  protocolVersion
14332
14332
  }) {
14333
- let { url: url2, auth } = extractAuthFromUrl(_origUrl);
14334
- const proxifiedURL = corsProxy ? corsProxify(corsProxy, url2) : url2;
14333
+ let { url: url3, auth } = extractAuthFromUrl(_origUrl);
14334
+ const proxifiedURL = corsProxy ? corsProxify(corsProxy, url3) : url3;
14335
14335
  if (auth.username || auth.password) {
14336
14336
  headers.Authorization = calculateBasicAuthHeader(auth);
14337
14337
  }
@@ -14353,7 +14353,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
14353
14353
  if (res.statusCode === 401 || res.statusCode === 203) {
14354
14354
  const getAuth = providedAuthBefore ? onAuthFailure : onAuth;
14355
14355
  if (getAuth) {
14356
- auth = await getAuth(url2, {
14356
+ auth = await getAuth(url3, {
14357
14357
  ...auth,
14358
14358
  headers: { ...headers }
14359
14359
  });
@@ -14366,7 +14366,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
14366
14366
  }
14367
14367
  }
14368
14368
  } else if (res.statusCode === 200 && providedAuthBefore && onAuthSuccess) {
14369
- await onAuthSuccess(url2, auth);
14369
+ await onAuthSuccess(url3, auth);
14370
14370
  }
14371
14371
  } while (tryAgain);
14372
14372
  if (res.statusCode !== 200) {
@@ -14409,22 +14409,22 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
14409
14409
  onProgress,
14410
14410
  corsProxy,
14411
14411
  service,
14412
- url: url2,
14412
+ url: url3,
14413
14413
  auth,
14414
14414
  body,
14415
14415
  headers,
14416
14416
  signal
14417
14417
  }) {
14418
- const urlAuth = extractAuthFromUrl(url2);
14419
- if (urlAuth) url2 = urlAuth.url;
14420
- if (corsProxy) url2 = corsProxify(corsProxy, url2);
14418
+ const urlAuth = extractAuthFromUrl(url3);
14419
+ if (urlAuth) url3 = urlAuth.url;
14420
+ if (corsProxy) url3 = corsProxify(corsProxy, url3);
14421
14421
  headers["content-type"] = `application/x-${service}-request`;
14422
14422
  headers.accept = `application/x-${service}-result`;
14423
14423
  updateHeaders(headers, auth);
14424
14424
  const res = await http.request({
14425
14425
  onProgress,
14426
14426
  method: "POST",
14427
- url: `${url2}/${service}`,
14427
+ url: `${url3}/${service}`,
14428
14428
  body,
14429
14429
  headers,
14430
14430
  signal
@@ -14446,33 +14446,33 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
14446
14446
  * @throws {UrlParseError} - If the URL cannot be parsed.
14447
14447
  * @throws {UnknownTransportError} - If the transport is not supported.
14448
14448
  */
14449
- static getRemoteHelperFor({ url: url2 }) {
14449
+ static getRemoteHelperFor({ url: url3 }) {
14450
14450
  const remoteHelpers = /* @__PURE__ */ new Map();
14451
14451
  remoteHelpers.set("http", GitRemoteHTTP);
14452
14452
  remoteHelpers.set("https", GitRemoteHTTP);
14453
- const parts = parseRemoteUrl({ url: url2 });
14453
+ const parts = parseRemoteUrl({ url: url3 });
14454
14454
  if (!parts) {
14455
- throw new UrlParseError(url2);
14455
+ throw new UrlParseError(url3);
14456
14456
  }
14457
14457
  if (remoteHelpers.has(parts.transport)) {
14458
14458
  return remoteHelpers.get(parts.transport);
14459
14459
  }
14460
14460
  throw new UnknownTransportError(
14461
- url2,
14461
+ url3,
14462
14462
  parts.transport,
14463
- parts.transport === "ssh" ? translateSSHtoHTTP(url2) : void 0
14463
+ parts.transport === "ssh" ? translateSSHtoHTTP(url3) : void 0
14464
14464
  );
14465
14465
  }
14466
14466
  };
14467
14467
  var scpLike = /^[^/@:]+@[^/@:]+:/;
14468
- function parseRemoteUrl({ url: url2 }) {
14469
- if (scpLike.test(url2)) {
14468
+ function parseRemoteUrl({ url: url3 }) {
14469
+ if (scpLike.test(url3)) {
14470
14470
  return {
14471
14471
  transport: "ssh",
14472
- address: url2
14472
+ address: url3
14473
14473
  };
14474
14474
  }
14475
- const matches = url2.match(/(\w+)(:\/\/|::)(.*)/);
14475
+ const matches = url3.match(/(\w+)(:\/\/|::)(.*)/);
14476
14476
  if (matches === null) return;
14477
14477
  if (matches[2] === "://") {
14478
14478
  return {
@@ -14583,9 +14583,9 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
14583
14583
  }
14584
14584
  function addCredentialUsername({ config: config2, onAuth }) {
14585
14585
  if (!onAuth) return onAuth;
14586
- return async (url2, auth) => {
14587
- const username = auth.username || await config2.get(`credential.${url2}.username`);
14588
- return onAuth(url2, username ? { ...auth, username } : auth);
14586
+ return async (url3, auth) => {
14587
+ const username = auth.username || await config2.get(`credential.${url3}.username`);
14588
+ return onAuth(url3, username ? { ...auth, username } : auth);
14589
14589
  };
14590
14590
  }
14591
14591
  function emptyPackfile(pack) {
@@ -14911,15 +14911,15 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
14911
14911
  const ref = _ref || await _currentBranch({ fs: fs2, gitdir, test: true });
14912
14912
  const config2 = await GitConfigManager.get({ fs: fs2, gitdir });
14913
14913
  const remote = _remote || ref && await config2.get(`branch.${ref}.remote`) || "origin";
14914
- const url2 = _url2 || await config2.get(`remote.${remote}.url`);
14915
- if (typeof url2 === "undefined") {
14914
+ const url3 = _url2 || await config2.get(`remote.${remote}.url`);
14915
+ if (typeof url3 === "undefined") {
14916
14916
  throw new MissingParameterError("remote OR url");
14917
14917
  }
14918
14918
  const remoteRef = _remoteRef || ref && await config2.get(`branch.${ref}.merge`) || _ref || "HEAD";
14919
14919
  if (corsProxy === void 0) {
14920
14920
  corsProxy = await config2.get("http.corsProxy");
14921
14921
  }
14922
- const GitRemoteHTTP2 = GitRemoteManager.getRemoteHelperFor({ url: url2 });
14922
+ const GitRemoteHTTP2 = GitRemoteManager.getRemoteHelperFor({ url: url3 });
14923
14923
  const remoteHTTP = await GitRemoteHTTP2.discover({
14924
14924
  http,
14925
14925
  onAuth: addCredentialUsername({ config: config2, onAuth }),
@@ -14927,7 +14927,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
14927
14927
  onAuthFailure: addCredentialUsername({ config: config2, onAuth: onAuthFailure }),
14928
14928
  corsProxy,
14929
14929
  service: "git-upload-pack",
14930
- url: url2,
14930
+ url: url3,
14931
14931
  headers,
14932
14932
  protocolVersion: 1
14933
14933
  });
@@ -15013,7 +15013,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
15013
15013
  onProgress,
15014
15014
  corsProxy,
15015
15015
  service: "git-upload-pack",
15016
- url: url2,
15016
+ url: url3,
15017
15017
  auth,
15018
15018
  body: [packbuffer],
15019
15019
  headers
@@ -15101,7 +15101,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
15101
15101
  const noun = fullref.startsWith("refs/tags") ? "tag" : "branch";
15102
15102
  response.FETCH_HEAD = {
15103
15103
  oid,
15104
- description: `${noun} '${abbreviateRef(fullref)}' of ${url2}`
15104
+ description: `${noun} '${abbreviateRef(fullref)}' of ${url3}`
15105
15105
  };
15106
15106
  if (onProgress || onMessage) {
15107
15107
  const lines = splitLines(response.progress);
@@ -15190,7 +15190,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
15190
15190
  onPostCheckout,
15191
15191
  dir,
15192
15192
  gitdir,
15193
- url: url2,
15193
+ url: url3,
15194
15194
  corsProxy,
15195
15195
  ref,
15196
15196
  remote,
@@ -15207,7 +15207,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
15207
15207
  }) {
15208
15208
  try {
15209
15209
  await _init({ fs: fs2, gitdir });
15210
- await _addRemote({ fs: fs2, gitdir, remote, url: url2, force: false });
15210
+ await _addRemote({ fs: fs2, gitdir, remote, url: url3, force: false });
15211
15211
  if (corsProxy) {
15212
15212
  const config2 = await GitConfigManager.get({ fs: fs2, gitdir });
15213
15213
  await config2.set(`http.corsProxy`, corsProxy);
@@ -15266,7 +15266,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
15266
15266
  onPostCheckout,
15267
15267
  dir,
15268
15268
  gitdir = join15(dir, ".git"),
15269
- url: url2,
15269
+ url: url3,
15270
15270
  corsProxy = void 0,
15271
15271
  ref = void 0,
15272
15272
  remote = "origin",
@@ -15289,7 +15289,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
15289
15289
  if (!noCheckout) {
15290
15290
  assertParameter("dir", dir);
15291
15291
  }
15292
- assertParameter("url", url2);
15292
+ assertParameter("url", url3);
15293
15293
  const fsp = new FileSystem(fs2);
15294
15294
  const updatedGitdir = await discoverGitdir({ fsp, dotgit: gitdir });
15295
15295
  return await _clone({
@@ -15304,7 +15304,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
15304
15304
  onPostCheckout,
15305
15305
  dir,
15306
15306
  gitdir: updatedGitdir,
15307
- url: url2,
15307
+ url: url3,
15308
15308
  corsProxy,
15309
15309
  ref,
15310
15310
  remote,
@@ -15781,7 +15781,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
15781
15781
  dir,
15782
15782
  gitdir,
15783
15783
  ref,
15784
- url: url2,
15784
+ url: url3,
15785
15785
  remote,
15786
15786
  remoteRef,
15787
15787
  prune,
@@ -15815,7 +15815,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
15815
15815
  gitdir,
15816
15816
  corsProxy,
15817
15817
  ref,
15818
- url: url2,
15818
+ url: url3,
15819
15819
  remote,
15820
15820
  remoteRef,
15821
15821
  singleBranch,
@@ -15864,7 +15864,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
15864
15864
  dir,
15865
15865
  gitdir = join15(dir, ".git"),
15866
15866
  ref,
15867
- url: url2,
15867
+ url: url3,
15868
15868
  remote,
15869
15869
  remoteRef,
15870
15870
  corsProxy,
@@ -15896,7 +15896,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
15896
15896
  dir,
15897
15897
  gitdir: updatedGitdir,
15898
15898
  ref,
15899
- url: url2,
15899
+ url: url3,
15900
15900
  remote,
15901
15901
  remoteRef,
15902
15902
  fastForwardOnly: true,
@@ -15924,7 +15924,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
15924
15924
  ref,
15925
15925
  remote,
15926
15926
  remoteRef,
15927
- url: url2,
15927
+ url: url3,
15928
15928
  corsProxy,
15929
15929
  depth = null,
15930
15930
  since = null,
@@ -15956,7 +15956,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
15956
15956
  ref,
15957
15957
  remote,
15958
15958
  remoteRef,
15959
- url: url2,
15959
+ url: url3,
15960
15960
  corsProxy,
15961
15961
  depth,
15962
15962
  since,
@@ -16078,14 +16078,14 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
16078
16078
  onAuthSuccess,
16079
16079
  onAuthFailure,
16080
16080
  corsProxy,
16081
- url: url2,
16081
+ url: url3,
16082
16082
  headers = {},
16083
16083
  forPush = false
16084
16084
  }) {
16085
16085
  try {
16086
16086
  assertParameter("http", http);
16087
- assertParameter("url", url2);
16088
- const GitRemoteHTTP2 = GitRemoteManager.getRemoteHelperFor({ url: url2 });
16087
+ assertParameter("url", url3);
16088
+ const GitRemoteHTTP2 = GitRemoteManager.getRemoteHelperFor({ url: url3 });
16089
16089
  const remote = await GitRemoteHTTP2.discover({
16090
16090
  http,
16091
16091
  onAuth,
@@ -16093,7 +16093,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
16093
16093
  onAuthFailure,
16094
16094
  corsProxy,
16095
16095
  service: forPush ? "git-receive-pack" : "git-upload-pack",
16096
- url: url2,
16096
+ url: url3,
16097
16097
  headers,
16098
16098
  protocolVersion: 1
16099
16099
  });
@@ -16143,15 +16143,15 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
16143
16143
  onAuthSuccess,
16144
16144
  onAuthFailure,
16145
16145
  corsProxy,
16146
- url: url2,
16146
+ url: url3,
16147
16147
  headers = {},
16148
16148
  forPush = false,
16149
16149
  protocolVersion = 2
16150
16150
  }) {
16151
16151
  try {
16152
16152
  assertParameter("http", http);
16153
- assertParameter("url", url2);
16154
- const GitRemoteHTTP2 = GitRemoteManager.getRemoteHelperFor({ url: url2 });
16153
+ assertParameter("url", url3);
16154
+ const GitRemoteHTTP2 = GitRemoteManager.getRemoteHelperFor({ url: url3 });
16155
16155
  const remote = await GitRemoteHTTP2.discover({
16156
16156
  http,
16157
16157
  onAuth,
@@ -16159,7 +16159,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
16159
16159
  onAuthFailure,
16160
16160
  corsProxy,
16161
16161
  service: forPush ? "git-receive-pack" : "git-upload-pack",
16162
- url: url2,
16162
+ url: url3,
16163
16163
  headers,
16164
16164
  protocolVersion
16165
16165
  });
@@ -16561,8 +16561,8 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
16561
16561
  const remoteNames = await config2.getSubsections("remote");
16562
16562
  const remotes = Promise.all(
16563
16563
  remoteNames.map(async (remote) => {
16564
- const url2 = await config2.get(`remote.${remote}.url`);
16565
- return { remote, url: url2 };
16564
+ const url3 = await config2.get(`remote.${remote}.url`);
16565
+ return { remote, url: url3 };
16566
16566
  })
16567
16567
  );
16568
16568
  return remotes;
@@ -16625,7 +16625,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
16625
16625
  onAuthSuccess,
16626
16626
  onAuthFailure,
16627
16627
  corsProxy,
16628
- url: url2,
16628
+ url: url3,
16629
16629
  headers = {},
16630
16630
  forPush = false,
16631
16631
  protocolVersion = 2,
@@ -16635,7 +16635,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
16635
16635
  }) {
16636
16636
  try {
16637
16637
  assertParameter("http", http);
16638
- assertParameter("url", url2);
16638
+ assertParameter("url", url3);
16639
16639
  const remote = await GitRemoteHTTP.discover({
16640
16640
  http,
16641
16641
  onAuth,
@@ -16643,7 +16643,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
16643
16643
  onAuthFailure,
16644
16644
  corsProxy,
16645
16645
  service: forPush ? "git-receive-pack" : "git-upload-pack",
16646
- url: url2,
16646
+ url: url3,
16647
16647
  headers,
16648
16648
  protocolVersion
16649
16649
  });
@@ -16657,7 +16657,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
16657
16657
  headers,
16658
16658
  corsProxy,
16659
16659
  service: forPush ? "git-receive-pack" : "git-upload-pack",
16660
- url: url2,
16660
+ url: url3,
16661
16661
  body
16662
16662
  });
16663
16663
  return parseListRefsResponse(res.body);
@@ -17114,7 +17114,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
17114
17114
  dir,
17115
17115
  gitdir = join15(dir, ".git"),
17116
17116
  ref,
17117
- url: url2,
17117
+ url: url3,
17118
17118
  remote,
17119
17119
  remoteRef,
17120
17120
  prune = false,
@@ -17159,7 +17159,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
17159
17159
  dir,
17160
17160
  gitdir: updatedGitdir,
17161
17161
  ref,
17162
- url: url2,
17162
+ url: url3,
17163
17163
  remote,
17164
17164
  remoteRef,
17165
17165
  fastForward: fastForward2,
@@ -17342,12 +17342,12 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
17342
17342
  }
17343
17343
  const config2 = await GitConfigManager.get({ fs: fs2, gitdir });
17344
17344
  remote = remote || await config2.get(`branch.${ref}.pushRemote`) || await config2.get("remote.pushDefault") || await config2.get(`branch.${ref}.remote`) || "origin";
17345
- const url2 = _url2 || await config2.get(`remote.${remote}.pushurl`) || await config2.get(`remote.${remote}.url`);
17346
- if (typeof url2 === "undefined") {
17345
+ const url3 = _url2 || await config2.get(`remote.${remote}.pushurl`) || await config2.get(`remote.${remote}.url`);
17346
+ if (typeof url3 === "undefined") {
17347
17347
  throw new MissingParameterError("remote OR url");
17348
17348
  }
17349
17349
  const remoteRef = _remoteRef || await config2.get(`branch.${ref}.merge`);
17350
- if (typeof url2 === "undefined") {
17350
+ if (typeof url3 === "undefined") {
17351
17351
  throw new MissingParameterError("remoteRef");
17352
17352
  }
17353
17353
  if (corsProxy === void 0) {
@@ -17355,7 +17355,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
17355
17355
  }
17356
17356
  const fullRef = await GitRefManager.expand({ fs: fs2, gitdir, ref });
17357
17357
  const oid = _delete ? "0000000000000000000000000000000000000000" : await GitRefManager.resolve({ fs: fs2, gitdir, ref: fullRef });
17358
- const GitRemoteHTTP2 = GitRemoteManager.getRemoteHelperFor({ url: url2 });
17358
+ const GitRemoteHTTP2 = GitRemoteManager.getRemoteHelperFor({ url: url3 });
17359
17359
  const httpRemote = await GitRemoteHTTP2.discover({
17360
17360
  http,
17361
17361
  onAuth: addCredentialUsername({ config: config2, onAuth }),
@@ -17363,7 +17363,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
17363
17363
  onAuthFailure: addCredentialUsername({ config: config2, onAuth: onAuthFailure }),
17364
17364
  corsProxy,
17365
17365
  service: "git-receive-pack",
17366
- url: url2,
17366
+ url: url3,
17367
17367
  headers,
17368
17368
  protocolVersion: 1,
17369
17369
  signal
@@ -17390,7 +17390,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
17390
17390
  if (onPrePush) {
17391
17391
  const hookCancel = await onPrePush({
17392
17392
  remote,
17393
- url: url2,
17393
+ url: url3,
17394
17394
  localRef: { ref: _delete ? "(delete)" : fullRef, oid },
17395
17395
  remoteRef: { ref: fullRemoteRef, oid: oldoid }
17396
17396
  });
@@ -17487,7 +17487,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
17487
17487
  onProgress,
17488
17488
  corsProxy,
17489
17489
  service: "git-receive-pack",
17490
- url: url2,
17490
+ url: url3,
17491
17491
  auth,
17492
17492
  headers,
17493
17493
  body: [...packstream1, ...packstream2],
@@ -17537,7 +17537,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
17537
17537
  ref,
17538
17538
  remoteRef,
17539
17539
  remote = "origin",
17540
- url: url2,
17540
+ url: url3,
17541
17541
  force = false,
17542
17542
  delete: _delete = false,
17543
17543
  corsProxy,
@@ -17565,7 +17565,7 @@ ${obj.gpgsig ? obj.gpgsig : ""}`;
17565
17565
  ref,
17566
17566
  remoteRef,
17567
17567
  remote,
17568
- url: url2,
17568
+ url: url3,
17569
17569
  force,
17570
17570
  delete: _delete,
17571
17571
  corsProxy,
@@ -19761,7 +19761,7 @@ var require_ignore2 = __commonJS({
19761
19761
  // cli/package.json
19762
19762
  var package_default = {
19763
19763
  name: "@docyrus/animaguru",
19764
- version: "0.16.0",
19764
+ version: "0.16.1",
19765
19765
  description: "Agent-friendly AnimaGuru CLI built with CrustJS",
19766
19766
  type: "module",
19767
19767
  bin: {
@@ -22817,16 +22817,16 @@ function serializeParams(options) {
22817
22817
  if (id2.includes(":") || id2.includes(";")) throw new TypeError('Invalid hyperlink id: ":" and ";" are reserved by the OSC 8 format.');
22818
22818
  return `id=${id2}`;
22819
22819
  }
22820
- function linkCode(url2, options) {
22821
- assertMatches(url2, "hyperlink URL", /^[\x21-\x7e]*$/, " without spaces");
22820
+ function linkCode(url3, options) {
22821
+ assertMatches(url3, "hyperlink URL", /^[\x21-\x7e]*$/, " without spaces");
22822
22822
  const params = serializeParams(options);
22823
22823
  return {
22824
- open: `${OSC}8;${params};${url2}${ST}`,
22824
+ open: `${OSC}8;${params};${url3}${ST}`,
22825
22825
  close: HYPERLINK_CLOSE
22826
22826
  };
22827
22827
  }
22828
- function link$1(text2, url2, options) {
22829
- return applyStyle(text2, linkCode(url2, options));
22828
+ function link$1(text2, url3, options) {
22829
+ return applyStyle(text2, linkCode(url3, options));
22830
22830
  }
22831
22831
  var dynamicColorKinds = ["fg", "bg"];
22832
22832
  function stepIsModifier(step) {
@@ -22966,9 +22966,9 @@ function createStyleInstance(options, runtime) {
22966
22966
  get colorDepth() {
22967
22967
  return resolveCapabilities().colorDepth;
22968
22968
  },
22969
- link(text2, url2, hyperlinkOptions) {
22970
- if (resolveCapabilities().modifiersEnabled) return link$1(text2, url2, hyperlinkOptions);
22971
- linkCode(url2, hyperlinkOptions);
22969
+ link(text2, url3, hyperlinkOptions) {
22970
+ if (resolveCapabilities().modifiersEnabled) return link$1(text2, url3, hyperlinkOptions);
22971
+ linkCode(url3, hyperlinkOptions);
22972
22972
  return text2;
22973
22973
  },
22974
22974
  ...Object.fromEntries(dynamicColorKinds.map((kind) => [kind, (...args) => {
@@ -24086,8 +24086,8 @@ function isNewerVersion(current, latest) {
24086
24086
  }
24087
24087
  async function fetchLatestVersion(packageName, registryUrl, timeoutMs) {
24088
24088
  try {
24089
- const url2 = `${registryUrl.replace(/\/+$/, "")}/${encodeURIComponent(packageName)}`;
24090
- const response = await fetch(url2, {
24089
+ const url3 = `${registryUrl.replace(/\/+$/, "")}/${encodeURIComponent(packageName)}`;
24090
+ const response = await fetch(url3, {
24091
24091
  signal: AbortSignal.timeout(timeoutMs),
24092
24092
  headers: { Accept: "application/vnd.npm.install-v1+json" }
24093
24093
  });
@@ -28228,21 +28228,21 @@ var asciiTabOrNewline = /[\t\n\r]/g;
28228
28228
  function stripTabAndNewline(value) {
28229
28229
  return value.replace(asciiTabOrNewline, "");
28230
28230
  }
28231
- function urlHostnameOk(url2, hostname3) {
28231
+ function urlHostnameOk(url3, hostname3) {
28232
28232
  hostname3.lastIndex = 0;
28233
- return hostname3.test(url2.hostname);
28233
+ return hostname3.test(url3.hostname);
28234
28234
  }
28235
- function urlProtocolOk(url2, protocol) {
28235
+ function urlProtocolOk(url3, protocol) {
28236
28236
  protocol.lastIndex = 0;
28237
- return protocol.test(url2.protocol.endsWith(":") ? url2.protocol.slice(0, -1) : url2.protocol);
28237
+ return protocol.test(url3.protocol.endsWith(":") ? url3.protocol.slice(0, -1) : url3.protocol);
28238
28238
  }
28239
28239
  var $ZodURL = /* @__PURE__ */ $constructor("$ZodURL", (inst, def) => {
28240
28240
  $ZodStringFormat.init(inst, def);
28241
28241
  inst._zod.check = (payload) => {
28242
28242
  try {
28243
28243
  const trimmed = payload.value.trim();
28244
- const url2 = validateURL(trimmed, def);
28245
- if (url2 === URL_BAD_FORMAT) {
28244
+ const url3 = validateURL(trimmed, def);
28245
+ if (url3 === URL_BAD_FORMAT) {
28246
28246
  payload.issues.push({
28247
28247
  code: "invalid_format",
28248
28248
  format: "url",
@@ -28253,7 +28253,7 @@ var $ZodURL = /* @__PURE__ */ $constructor("$ZodURL", (inst, def) => {
28253
28253
  });
28254
28254
  return;
28255
28255
  }
28256
- if (url2 === URL_UNPARSEABLE) {
28256
+ if (url3 === URL_UNPARSEABLE) {
28257
28257
  payload.issues.push({
28258
28258
  code: "invalid_format",
28259
28259
  format: "url",
@@ -28263,11 +28263,11 @@ var $ZodURL = /* @__PURE__ */ $constructor("$ZodURL", (inst, def) => {
28263
28263
  });
28264
28264
  return;
28265
28265
  }
28266
- if (url2 === true) {
28266
+ if (url3 === true) {
28267
28267
  payload.value = stripTabAndNewline(trimmed);
28268
28268
  return;
28269
28269
  }
28270
- if (def.hostname && !urlHostnameOk(url2, def.hostname)) {
28270
+ if (def.hostname && !urlHostnameOk(url3, def.hostname)) {
28271
28271
  payload.issues.push({
28272
28272
  code: "invalid_format",
28273
28273
  format: "url",
@@ -28278,7 +28278,7 @@ var $ZodURL = /* @__PURE__ */ $constructor("$ZodURL", (inst, def) => {
28278
28278
  continue: !def.abort
28279
28279
  });
28280
28280
  }
28281
- if (def.protocol && !urlProtocolOk(url2, def.protocol)) {
28281
+ if (def.protocol && !urlProtocolOk(url3, def.protocol)) {
28282
28282
  payload.issues.push({
28283
28283
  code: "invalid_format",
28284
28284
  format: "url",
@@ -28289,7 +28289,7 @@ var $ZodURL = /* @__PURE__ */ $constructor("$ZodURL", (inst, def) => {
28289
28289
  continue: !def.abort
28290
28290
  });
28291
28291
  }
28292
- payload.value = def.normalize ? url2.href : stripTabAndNewline(trimmed);
28292
+ payload.value = def.normalize ? url3.href : stripTabAndNewline(trimmed);
28293
28293
  return;
28294
28294
  } catch (_) {
28295
28295
  payload.issues.push({
@@ -45105,10 +45105,10 @@ import { dirname as dirname3, join as join4, resolve as resolve3 } from "node:pa
45105
45105
  import { homedir as homedir3 } from "node:os";
45106
45106
  import { createHash, randomUUID } from "node:crypto";
45107
45107
  function serverUrl(value) {
45108
- const url2 = new URL(value);
45109
- if (url2.username || url2.password || url2.search || url2.hash || url2.pathname !== "/") throw new Error("Use an origin URL without credentials, path, query or fragment.");
45110
- if (url2.protocol !== "https:" && !(url2.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(url2.hostname))) throw new Error("Use HTTPS, except for local development.");
45111
- return url2.origin;
45108
+ const url3 = new URL(value);
45109
+ if (url3.username || url3.password || url3.search || url3.hash || url3.pathname !== "/") throw new Error("Use an origin URL without credentials, path, query or fragment.");
45110
+ if (url3.protocol !== "https:" && !(url3.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(url3.hostname))) throw new Error("Use HTTPS, except for local development.");
45111
+ return url3.origin;
45112
45112
  }
45113
45113
  function credentialsPath() {
45114
45114
  return resolve3(process.env.ANIMAGURU_CONFIG_DIR ?? join4(homedir3(), ".config", "animaguru"), "credentials.json");
@@ -46168,7 +46168,7 @@ async function createVideoProject(options, installer = installDependencies) {
46168
46168
  const manifest = JSON.parse(await readFile4(packagePath, "utf8"));
46169
46169
  manifest.name = basename2(directory).toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 100) || "animaguru-video";
46170
46170
  await writeFile6(packagePath, JSON.stringify(manifest, null, 2) + "\n");
46171
- const skills = [.../* @__PURE__ */ new Set(["animaguru", "animaguru-video", "animaguru-brand-kit", ...VIDEO_PURPOSE_SKILLS, ...VIDEO_TEMPLATES.map((template) => template.skill)])];
46171
+ const skills = [.../* @__PURE__ */ new Set(["animaguru", "animaguru-video", "animaguru-brand-kit", "animaguru-cutcraft", ...VIDEO_PURPOSE_SKILLS, ...VIDEO_TEMPLATES.map((template) => template.skill)])];
46172
46172
  const pixiSkillsRoot = join6(packageRoot, "vendor/pixijs-skills/skills");
46173
46173
  const pixiSkills = (await readdir2(pixiSkillsRoot, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
46174
46174
  for (const agent of [".agents", ".claude"]) {
@@ -46402,6 +46402,9 @@ var assetRequest = external_exports.discriminatedUnion("type", [
46402
46402
  }).transform((p) => ({ ...p, voice: p.voice || (isElevenLabsSpeech(p.model) ? DEFAULT_ELEVENLABS_VOICE : "Kore") })),
46403
46403
  external_exports.object({ ...base, type: external_exports.literal("sfx"), model: external_exports.literal("eleven_text_to_sound_v2").default("eleven_text_to_sound_v2"), seconds: external_exports.number().min(0.5).max(30).default(5), loop: external_exports.boolean().default(false), promptInfluence: external_exports.number().min(0).max(1).default(0.3) })
46404
46404
  ]);
46405
+ var LIBRARY_AUDIO_MODEL = "library-audio";
46406
+ var libraryAudio = external_exports.object({ name: base.name, prompt: external_exports.string().trim().max(4e3).default(""), type: external_exports.enum(["music", "sfx"]), model: external_exports.literal(LIBRARY_AUDIO_MODEL), loop: external_exports.boolean().default(false) });
46407
+ var savedAssetOptions = external_exports.union([assetRequest, libraryAudio]);
46405
46408
  var speechCharacters = (text2) => Array.from(text2).length;
46406
46409
  function assetEstimate(p, now = Date.now()) {
46407
46410
  if (p.type === "image") return Math.ceil(130 * spriteImageModel(p.model).relCost);
@@ -47088,7 +47091,7 @@ var avatarTemplate = (id2) => AVATAR_LOOK_TEMPLATES.find((t) => t.id === id2);
47088
47091
  var heygenId = external_exports.string().trim().regex(/^(?!\.{1,2}$)[A-Za-z0-9_.-]{1,128}$/, "Use an avatar id returned by AnimaGuru.");
47089
47092
  var sha = external_exports.string().regex(/^[a-f0-9]{64}$/);
47090
47093
  var name = external_exports.string().trim().min(1).max(80);
47091
- var https = external_exports.string().trim().max(2048).url().refine((url2) => url2.startsWith("https://"), "Use an HTTPS URL.");
47094
+ var https = external_exports.string().trim().max(2048).url().refine((url3) => url3.startsWith("https://"), "Use an HTTPS URL.");
47092
47095
  var AVATAR_INLINE_MAX_BYTES = 8 * 1024 * 1024;
47093
47096
  var AVATAR_PHOTO_TYPES = ["image/png", "image/jpeg", "image/webp"];
47094
47097
  var AVATAR_FOOTAGE_TYPES = ["video/mp4", "video/quicktime", "video/webm"];
@@ -47458,10 +47461,10 @@ async function providerError(response) {
47458
47461
  if (response.status === 403) return new YouTubeError("YouTube denied this request. Check channel permissions and your Google project's YouTube API configuration.", false, false, reason);
47459
47462
  return new YouTubeError(`YouTube request failed (${response.status}${reason ? `: ${reason}` : ""}).`, response.status === 429 || response.status >= 500, false, reason);
47460
47463
  }
47461
- async function googleFetch(url2, init, signal) {
47464
+ async function googleFetch(url3, init, signal) {
47462
47465
  signal?.throwIfAborted();
47463
47466
  try {
47464
- return await fetch(url2, { ...init, redirect: "manual", signal: AbortSignal.any([AbortSignal.timeout(18e4), ...signal ? [signal] : []]) });
47467
+ return await fetch(url3, { ...init, redirect: "manual", signal: AbortSignal.any([AbortSignal.timeout(18e4), ...signal ? [signal] : []]) });
47465
47468
  } catch {
47466
47469
  signal?.throwIfAborted();
47467
47470
  throw new YouTubeError("YouTube connection interrupted.", true);
@@ -47493,8 +47496,8 @@ async function accessToken(credentials, signal, force = false) {
47493
47496
  await saveYouTubeCredentials(credentials);
47494
47497
  return credentials.accessToken;
47495
47498
  }
47496
- async function youtubeRequest(credentials, url2, init = {}, signal) {
47497
- const send = (token3) => googleFetch(url2, { ...init, headers: { ...init.headers, Authorization: `Bearer ${token3}` } }, signal);
47499
+ async function youtubeRequest(credentials, url3, init = {}, signal) {
47500
+ const send = (token3) => googleFetch(url3, { ...init, headers: { ...init.headers, Authorization: `Bearer ${token3}` } }, signal);
47498
47501
  let response = await send(await accessToken(credentials, signal));
47499
47502
  if (response.status === 401) {
47500
47503
  await response.body?.cancel();
@@ -47567,26 +47570,26 @@ async function youtubeLogin(options) {
47567
47570
  response.setHeader("Referrer-Policy", "no-referrer");
47568
47571
  response.setHeader("Content-Type", "text/plain; charset=UTF-8");
47569
47572
  response.setHeader("Content-Security-Policy", "default-src 'none'");
47570
- let url2;
47573
+ let url3;
47571
47574
  try {
47572
- url2 = new URL(request2.url ?? "/", redirectUri);
47575
+ url3 = new URL(request2.url ?? "/", redirectUri);
47573
47576
  } catch {
47574
47577
  response.writeHead(400).end("Invalid request.");
47575
47578
  return;
47576
47579
  }
47577
- if (request2.method !== "GET" || request2.headers.host !== new URL(redirectUri).host || url2.pathname !== "/oauth2callback" || (request2.url?.length ?? 0) > 8192) {
47580
+ if (request2.method !== "GET" || request2.headers.host !== new URL(redirectUri).host || url3.pathname !== "/oauth2callback" || (request2.url?.length ?? 0) > 8192) {
47578
47581
  response.writeHead(404).end("Not found.");
47579
47582
  return;
47580
47583
  }
47581
- const returnedState = Buffer.from(url2.searchParams.get("state") ?? "");
47584
+ const returnedState = Buffer.from(url3.searchParams.get("state") ?? "");
47582
47585
  const expectedState = Buffer.from(state);
47583
47586
  if (used || returnedState.length !== expectedState.length || !timingSafeEqual(returnedState, expectedState)) {
47584
47587
  response.writeHead(400).end("Invalid authorization state.");
47585
47588
  return;
47586
47589
  }
47587
47590
  used = true;
47588
- const code = url2.searchParams.get("code");
47589
- if (url2.searchParams.has("error") || !code || code.length > 4096) {
47591
+ const code = url3.searchParams.get("code");
47592
+ if (url3.searchParams.has("error") || !code || code.length > 4096) {
47590
47593
  response.writeHead(400).end("Authorization cancelled. Return to your terminal.");
47591
47594
  fail(new Error("YouTube authorization cancelled."));
47592
47595
  return;
@@ -47607,11 +47610,11 @@ async function youtubeLogin(options) {
47607
47610
  let token3;
47608
47611
  try {
47609
47612
  options.signal?.throwIfAborted();
47610
- const url2 = new URL("https://accounts.google.com/o/oauth2/v2/auth");
47611
- url2.search = new URLSearchParams({ client_id: client.clientId, redirect_uri: redirectUri, response_type: "code", scope: requested.join(" "), state, code_challenge: createHash4("sha256").update(verifier).digest("base64url"), code_challenge_method: "S256", access_type: "offline", prompt: "consent select_account" }).toString();
47613
+ const url3 = new URL("https://accounts.google.com/o/oauth2/v2/auth");
47614
+ url3.search = new URLSearchParams({ client_id: client.clientId, redirect_uri: redirectUri, response_type: "code", scope: requested.join(" "), state, code_challenge: createHash4("sha256").update(verifier).digest("base64url"), code_challenge_method: "S256", access_type: "offline", prompt: "consent select_account" }).toString();
47612
47615
  options.log?.("YouTube authorization stays on this machine. Tokens and upload receipts are saved in owner-only local files. Video bytes go directly to Google. Google privacy: https://policies.google.com/privacy \xB7 YouTube terms: https://www.youtube.com/t/terms");
47613
- options.log?.(`Open ${url2.href}`);
47614
- if (options.browser !== false) await options.openBrowser?.(url2.href);
47616
+ options.log?.(`Open ${url3.href}`);
47617
+ if (options.browser !== false) await options.openBrowser?.(url3.href);
47615
47618
  const body = await tokenRequest(client, { grant_type: "authorization_code", code: await callback, code_verifier: verifier, redirect_uri: redirectUri }, options.signal);
47616
47619
  token3 = typeof body.refresh_token === "string" ? body.refresh_token : typeof body.access_token === "string" ? body.access_token : void 0;
47617
47620
  const scopes = typeof body.scope === "string" ? body.scope.split(" ") : [];
@@ -47746,7 +47749,7 @@ function fromStream(stream) {
47746
47749
  }
47747
47750
  async function request({
47748
47751
  onProgress,
47749
- url: url2,
47752
+ url: url3,
47750
47753
  method = "GET",
47751
47754
  headers = {},
47752
47755
  fetchOptions = {},
@@ -47756,7 +47759,7 @@ async function request({
47756
47759
  if (body) {
47757
47760
  body = await collect(body);
47758
47761
  }
47759
- const res = await fetch(url2, {
47762
+ const res = await fetch(url3, {
47760
47763
  ...fetchOptions,
47761
47764
  method,
47762
47765
  headers,
@@ -47947,13 +47950,13 @@ async function withLock(root, run) {
47947
47950
  await rm2(path, { force: true });
47948
47951
  }
47949
47952
  }
47950
- async function openContext(dir, options, url2) {
47953
+ async function openContext(dir, options, url3) {
47951
47954
  const root = await cloudRootFor(dir);
47952
47955
  const link2 = await readJson2(join11(root, LINK_FILE));
47953
47956
  if (!link2) throw new Error("This folder is not linked to a cloud project. Run animaguru cloud link, or animaguru cloud clone ID.");
47954
47957
  if (link2.schemaVersion !== 1 || !cloudProjectId.safeParse(link2.projectId).success) throw new Error(`Unsupported cloud link in ${LINK_FILE}. Run animaguru cloud unlink and link again.`);
47955
47958
  const state = await readJson2(join11(root, STATE_FILE)) ?? { schemaVersion: 1, base: null, files: {} };
47956
- return { root, gitdir: join11(root, GIT_DIR), link: link2, state, client: new Client(url2 ?? link2.origin, options.signal), options };
47959
+ return { root, gitdir: join11(root, GIT_DIR), link: link2, state, client: new Client(url3 ?? link2.origin, options.signal), options };
47957
47960
  }
47958
47961
  async function cloudLink(dir) {
47959
47962
  const link2 = await readJson2(join11(await cloudRootFor(dir), LINK_FILE));
@@ -48431,14 +48434,14 @@ async function restoreCloud(options) {
48431
48434
  // cli/src/youtube-upload.ts
48432
48435
  var YOUTUBE_CHUNK_BYTES = 8 * 1024 * 1024;
48433
48436
  function safeYouTubeSession(value) {
48434
- let url2;
48437
+ let url3;
48435
48438
  try {
48436
- url2 = new URL(value);
48439
+ url3 = new URL(value);
48437
48440
  } catch {
48438
48441
  throw new YouTubeError("Google returned an invalid upload session.");
48439
48442
  }
48440
- if (url2.protocol !== "https:" || !["www.googleapis.com", "youtube.googleapis.com"].includes(url2.hostname) || url2.port || url2.username || url2.password || url2.hash || !/^\/upload\/youtube\/v3\/videos\/?$/.test(url2.pathname) || !url2.searchParams.get("upload_id")) throw new YouTubeError("Google returned an invalid upload session.");
48441
- return url2.href;
48443
+ if (url3.protocol !== "https:" || !["www.googleapis.com", "youtube.googleapis.com"].includes(url3.hostname) || url3.port || url3.username || url3.password || url3.hash || !/^\/upload\/youtube\/v3\/videos\/?$/.test(url3.pathname) || !url3.searchParams.get("upload_id")) throw new YouTubeError("Google returned an invalid upload session.");
48444
+ return url3.href;
48442
48445
  }
48443
48446
  async function inspectMp4(file2, signal) {
48444
48447
  const handle = await open6(file2, "r");
@@ -48632,14 +48635,14 @@ async function completed(job, response) {
48632
48635
  return youtubeJobSummary(job);
48633
48636
  }
48634
48637
  async function transfer(credentials, job, source, options) {
48635
- const url2 = safeYouTubeSession(job.sessionUrl);
48638
+ const url3 = safeYouTubeSession(job.sessionUrl);
48636
48639
  let attempts = 0;
48637
48640
  let stalled = 0;
48638
48641
  try {
48639
48642
  while (true) {
48640
48643
  await source.check();
48641
48644
  try {
48642
- const probe = await youtubeRequest(credentials, url2, { method: "PUT", headers: { "Content-Range": `bytes */${job.bytes}`, "Content-Length": "0" }, body: Buffer.alloc(0) }, options.signal);
48645
+ const probe = await youtubeRequest(credentials, url3, { method: "PUT", headers: { "Content-Range": `bytes */${job.bytes}`, "Content-Length": "0" }, body: Buffer.alloc(0) }, options.signal);
48643
48646
  if (probe.ok) return await completed(job, probe);
48644
48647
  const offset = receivedBytes(probe, job.bytes);
48645
48648
  await probe.body?.cancel();
@@ -48648,7 +48651,7 @@ async function transfer(credentials, job, source, options) {
48648
48651
  const read2 = await source.handle.read(bytes, 0, bytes.length, offset);
48649
48652
  if (read2.bytesRead !== bytes.length) throw new Error("The MP4 changed during upload. Restore it before resuming.");
48650
48653
  await source.check();
48651
- const response = await youtubeRequest(credentials, url2, { method: "PUT", headers: { "Content-Type": "video/mp4", "Content-Length": String(bytes.length), "Content-Range": `bytes ${offset}-${offset + bytes.length - 1}/${job.bytes}` }, body: new Uint8Array(bytes) }, options.signal);
48654
+ const response = await youtubeRequest(credentials, url3, { method: "PUT", headers: { "Content-Type": "video/mp4", "Content-Length": String(bytes.length), "Content-Range": `bytes ${offset}-${offset + bytes.length - 1}/${job.bytes}` }, body: new Uint8Array(bytes) }, options.signal);
48652
48655
  if (response.ok) return await completed(job, response);
48653
48656
  const uploaded = receivedBytes(response, job.bytes);
48654
48657
  await response.body?.cancel();
@@ -48891,18 +48894,32 @@ var metadataFields = {
48891
48894
  /** Facts the server cannot read from the file, such as tempo or mood; they override what it measured. */
48892
48895
  details: detailsInput.default({})
48893
48896
  };
48897
+ var LIBRARY_UPLOAD_MAX_BYTES = 95 * 1024 * 1024;
48898
+ var libraryBlobSha = external_exports.string().regex(/^[a-f0-9]{64}$/, "Use the file's lowercase hex SHA-256.");
48899
+ var url2 = external_exports.string().trim().url().max(500);
48900
+ var libraryOrigin = external_exports.object({
48901
+ name: text(80),
48902
+ url: url2,
48903
+ author: external_exports.string().trim().min(1).max(120).optional(),
48904
+ authorUrl: url2.optional(),
48905
+ license: text(80),
48906
+ licenseUrl: url2.optional()
48907
+ });
48894
48908
  var libraryPublishBody = external_exports.object({
48895
48909
  /** Defaults to the title as a slug. */
48896
48910
  id: slug.optional(),
48897
- projectId: external_exports.string().regex(/^prj_[A-Za-z0-9]{8,40}$/),
48911
+ /** The project an asset or sprite comes from; uploads have none. */
48912
+ projectId: external_exports.string().regex(/^prj_[A-Za-z0-9]{8,40}$/).optional(),
48898
48913
  source: external_exports.discriminatedUnion("kind", [
48899
48914
  external_exports.object({ kind: external_exports.literal("asset"), assetId: external_exports.string().uuid() }),
48900
- external_exports.object({ kind: external_exports.literal("sprite"), character: slug })
48915
+ external_exports.object({ kind: external_exports.literal("sprite"), character: slug }),
48916
+ /** Audio uploaded with PUT /api/v1/asset-library/uploads/SHA256, e.g. a CC0 recording, with who made it. */
48917
+ external_exports.object({ kind: external_exports.literal("upload"), type: external_exports.enum(["music", "sfx"]), sha: libraryBlobSha, origin: libraryOrigin })
48901
48918
  ]),
48902
48919
  /** Replace an existing entry with the same id. */
48903
48920
  replace: external_exports.boolean().default(false),
48904
48921
  ...metadataFields
48905
- });
48922
+ }).refine((body) => body.source.kind === "upload" || body.projectId, { message: "Choose a project.", path: ["projectId"] });
48906
48923
  var libraryUpdateBody = external_exports.object({ ...metadataFields, keywords: keywordList, details: detailsInput }).partial().refine((value) => Object.keys(value).length > 0, "Change at least one field.");
48907
48924
 
48908
48925
  // cli/src/library.ts
@@ -49025,9 +49042,9 @@ var workspaceQuery = async (client) => {
49025
49042
  const selected = await selectedWorkspace(client.origin);
49026
49043
  return selected === "me" ? "" : `?workspace=${encodeURIComponent(selected)}`;
49027
49044
  };
49028
- async function openBrowser(url2) {
49045
+ async function openBrowser(url3) {
49029
49046
  const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "rundll32" : "xdg-open";
49030
- const args = process.platform === "win32" ? ["url.dll,FileProtocolHandler", url2] : [url2];
49047
+ const args = process.platform === "win32" ? ["url.dll,FileProtocolHandler", url3] : [url3];
49031
49048
  await new Promise((resolve15) => {
49032
49049
  const child = spawn3(command, args, { stdio: "ignore", detached: true });
49033
49050
  child.once("error", () => resolve15());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@docyrus/animaguru",
3
- "version": "0.16.0",
3
+ "version": "0.16.1",
4
4
  "description": "Agent-friendly AnimaGuru CLI built with CrustJS",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,48 @@
1
+ ---
2
+ name: animaguru-cutcraft
3
+ description: Create AnimaGuru Cutcraft explainers and product stories with tactile paper collage, precise editorial typography, purposeful motion, and AnimaGuru-generated media. Use for this visual treatment or a Vox-inspired explainer adapted to AnimaGuru.
4
+ ---
5
+
6
+ # AnimaGuru Cutcraft
7
+
8
+ Make an original editorial film that explains an idea through things the viewer can see move, connect, separate and resolve. Use AnimaGuru for generation and a controlled composition for titles, labels, timing and branding. The style can serve another product; use that product's facts and identity.
9
+
10
+ ## Establish the film
11
+
12
+ Reuse the user's subject, references, language, placement and generation authorization. Resolve only consequential missing choices. For an exploratory sample, state sensible assumptions and proceed: a short English landscape film with a single narrator is a useful starting point, not a requirement for every film.
13
+
14
+ Read first-party product material before writing claims. Separate actual product footage from conceptual illustrations in the production records. Avoid unverified performance claims, invented customer evidence, and promises of perfect AI results. End an advertisement with a relevant invitation; end an educational explainer on its insight unless a CTA was requested.
15
+
16
+ Write a concise narration and a beat sheet pairing each spoken idea with a visible event. Duration follows the measured voice and the user's brief. A problem, a concrete mechanism, an outcome and a short close can work for a product film; choose the structure that fits the subject.
17
+
18
+ ## The Cutcraft visual language
19
+
20
+ - **Material:** matte colored cardstock, visible fibers, slightly irregular scissor edges and short directional contact shadows. Layer flat pieces; keep silhouettes readable. Avoid glossy plastic and unrelated photorealistic environments.
21
+ - **AnimaGuru palette:** warm cream `#F7F2E8`, ink `#162B38`, brand blue `#2C6BD0`, orange `#E67D43`, with restrained sage `#93AA8B`. For client films, replace accents with the client's brand. Backgrounds should have quiet areas for type.
22
+ - **Typography:** one bold editorial heading family and a compatible reading face. Use actual local fonts, vector logos and rendered text for names, numbers and claims. Keep generated footage free of essential lettering. Short labels summarize; narration carries the full sentence.
23
+ - **Composition:** one dominant idea per beat, useful negative space, visibly separated foreground and background. A print-like grid, small folio marks and a fine blue connector line give the film its own identity. Avoid copying Vox's logo, layouts or other identifying brand elements.
24
+ - **Motion:** cutouts slide, rotate at plausible paper joints, pin into place, peel, stack or reveal. A connector is drawn when a relationship becomes clear. Motion answers the spoken line; texture and camera movement remain restrained.
25
+ - **Continuity:** lock material, light direction, palette, camera language and recurring character description. An orange fox with a cobalt scarf is an optional AnimaGuru motif, not compulsory casting for other subjects.
26
+ - **Sound:** one narrator across the film, a quiet instrumental bed and a few motivated paper taps or rustles. Keep speech intelligible and let the closing thought breathe.
27
+
28
+ Build a visual explanation instead of using the same decorative collage under every sentence. If illustrating control, visibly move one object while its surroundings stay fixed. If explaining a connected workflow, show the pieces joining in the order the narration names them.
29
+
30
+ ## Generate with AnimaGuru
31
+
32
+ Read [the generation adapter](references/generation.md) when producing media. It covers CLI discovery, supported references, credit estimates, continuity and local assembly. Do not substitute MaxFusion calls or assume a Google Omni model exists.
33
+
34
+ Prefer the installed AnimaGuru CLI for local asset work; use connected MCP tools when they are available and appropriate. Creating this skill is not generation authorization. A user request to generate the film authorizes a proportionate initial attempt; honor stated budgets and surface a concrete estimate before submission. Do not purchase credits or change provider defaults as part of film production.
35
+
36
+ Generate artwork and motion separately from exact type. For a short trial, a few strong compositions with changing editorial labels usually communicate more than many loosely related clips. Reuse good existing textures, music or SFX when they fit. A generated clip may be one layer inside a controlled film rather than the entire frame.
37
+
38
+ For precise placement or when video providers are unavailable, generate isolated transparent paper subjects and animate their transforms in the composition. Build the fixed setting from separate paper layers. This is a complete production route: character movement, path reveals, clip placement and typography can remain editable without a video-model call. Describe it as animation of generated artwork, not as AI-generated video footage.
39
+
40
+ Use a single speech asset or consistently configured segments. Measure the recording before finalizing scenes; do not assume the requested pacing was achieved. Replace or mute incidental speech from video generation when using the separate narrator.
41
+
42
+ ## Review and deliver
43
+
44
+ Keep the brief, narration, scene timings, request settings, estimates, job IDs, returned asset IDs, costs and source links together with the film's production files. Preserve an editable composition or reproducible render recipe and local copies of the media.
45
+
46
+ Inspect the actual opening, middle, transitions and closing frames. Check exact brand spelling, readable labels, character consistency, contrast, crop and any model artifacts. Check audio duration and clipping; listen when playback tools permit, and describe listening limitations honestly. Repair the composition before buying another generation when timing, crop or typography can solve the problem.
47
+
48
+ Export a playable local film and report what was generated, what was reused, and any remaining defects. Return a preview, the final file, and a link to the skill. Creating a film does not imply publishing it publicly or changing the product website.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "AnimaGuru Cutcraft"
3
+ short_description: "Paper collage explainers made with AnimaGuru"
4
+ default_prompt: "Use $animaguru-cutcraft to make a short AnimaGuru explainer with tactile paper collage and precise editorial motion."
@@ -0,0 +1,61 @@
1
+ # AnimaGuru generation adapter
2
+
3
+ ## Discover the actual environment
4
+
5
+ For CLI work, inspect `animaguru --help`, `animaguru schema`, `animaguru auth status`, `animaguru credits` and `animaguru catalog`. Do not read credential files. If it is absent, use connected MCP or obtain agreement to install the CLI; installation and authentication are separate actions.
6
+
7
+ List projects before choosing one, then inspect the selected project and `assets list PROJECT_ID`. Reuse the user's project where appropriate. Use returned project IDs, asset IDs and file SHAs. Create a separate project when the film needs its own asset container. Saved filenames are not file SHAs.
8
+
9
+ The CLI catalog describes known capabilities; the live project's `availableModels` and `videoProfiles` describe enabled models. Choose from their intersection. A local `--dry-run` can succeed for a disabled server model, so it does not prove provider availability.
10
+
11
+ With MCP, discover the equivalent project, catalog, assets, estimate, generate and job tools. If account linking is needed, let the user complete it. Never invent tool calls or fall back to database access.
12
+
13
+ ## Plan and estimate
14
+
15
+ Save requests as JSON and validate each with `assets generate ... --dry-run` or a batch with `assets batch PROJECT_ID --file REQUESTS.json --dry-run`. Add all estimates, including speech and any music/SFX, before the first submission. These are AnimaGuru credits; compare returned amounts directly with the authorized credit budget. Actual usage can differ from estimates.
16
+
17
+ Use only a model and options returned by the live catalog. An image can establish an art direction or become a video reference. An illustrative prompt shell is:
18
+
19
+ > Original tactile editorial paper collage: matte warm cream cardstock, cobalt blue and fox-orange cutouts, visible paper fibers, slightly imperfect scissor edges, soft short shadows falling down-right. [One concrete composition and action.] Restrained studio lighting and readable silhouettes. Keep a quiet area for separately composed editorial type. No lettering, captions, logos or watermark.
20
+
21
+ Describe the subject, position, scale and material of each important element. Keep recurring character features verbatim between requests. Exact logos and text belong in the composition, not in model-generated pixels.
22
+
23
+ ## Submit and track
24
+
25
+ For example, after replacing placeholders with discovered values and validating the settings:
26
+
27
+ ```sh
28
+ animaguru assets generate PROJECT_ID --type image --name "cutcraft-keyframe" \
29
+ --model IMAGE_MODEL --aspect-ratio 3:2 --prompt "SAVED ART DIRECTION" --dry-run
30
+ animaguru assets generate PROJECT_ID --type video --name "cutcraft-scene" \
31
+ --model VIDEO_MODEL --workflow image-to-video --reference IMAGE_FILE_SHA \
32
+ --seconds 8 --resolution 768p --mode speed \
33
+ --prompt "SAVED VISUAL ACTION" --dry-run
34
+ ```
35
+
36
+ The literal values above are examples, not assumed capabilities. Image aspect ratios, video modes, duration limits, soundtrack handling and audio support vary by model. Add `--yes` only for the user's authorized work. Save the queued receipt immediately. Submission results may be job receipts rather than completed assets.
37
+
38
+ Use `jobs list PROJECT_ID` and `jobs wait PROJECT_ID JOB_ID --timeout SECONDS` to track the original job. A timeout does not cancel it. Check the job and saved assets before resubmitting after an uncertain response. Do not repeatedly regenerate failed work without diagnosing the error and checking the remaining budget.
39
+
40
+ When a job explicitly fails because its provider is unavailable, preserve that receipt and inspect models from another provider. A replacement with compatible capabilities and an estimate inside the authorized scope can continue the task. Remove unsupported options when changing providers; a seed or prompt-expansion setting valid for one model may be invalid for another. Relay the returned charge or refund status without assuming all failed jobs are free.
41
+
42
+ Download finished assets with `assets download PROJECT_ID ASSET_ID --output FILE`. Keep stdout receipts separate from media and never overwrite an existing downloaded file inadvertently.
43
+
44
+ ## Choose continuity deliberately
45
+
46
+ - For different explanatory ideas, use intentional cuts, a shared palette and a common layout. An unrelated next scene does not need the previous clip's last frame.
47
+ - For one continuous action, use a supported video extension workflow with the saved video file SHA as `--source`. Inspect the catalog for its resolution and duration limits. `extendOutput` distinguishes the combined video from continuation-only output; inspect and trim any returned overlap rather than counting it twice.
48
+ - First/last-frame workflows use `--reference` and `--end-reference` only when the model profile supports them. Both must be saved files belonging to the selected project.
49
+ - A locally extracted end frame is not automatically a project asset. Use an explicitly supported upload/import path before referencing it; do not invent an extract-frame or raw upload MCP tool. A character-import command can redraw and charge credits, so it is not a transparent generic frame upload.
50
+
51
+ Do not carry over the reference workflow's fixed ten-second clip assumption. Choose durations from the model profile and the story, and measure the returned files.
52
+
53
+ ## Narration and assembly
54
+
55
+ Generate one narrator with `assets generate ... --type speech --voice VOICE --delivery INSTRUCTIONS --prompt TRANSCRIPT`, after discovering the available speech model and voice. Use delivery instructions supported by that provider. Keep the spoken transcript independent of the visual directions. Download and measure the actual recording with `ffprobe`.
56
+
57
+ For generated motion layers, mute their sound when it conflicts with the separate narration. Reuse or generate a compatible instrumental bed at low level. Keep clip selection, crop, text, logos and all timing deterministic in the composition. Use an existing AnimaGuru HTML/Pixi film workspace when one is supplied; otherwise choose a local composition/export tool suited to the requested deliverable. Keep source files and a reproducible export command.
58
+
59
+ For controlled cutout animation, request `transparent: true` image assets (CLI `--transparent`) and check the downloaded alpha channel. Keep a subject's complete silhouette inside the image. Animate these assets as separate layers against a fixed setting; drive position, rotation, entrances and holds from absolute time. Use actual transparent silhouettes for a moving subject instead of moving an opaque rectangular illustration. If video providers are all unavailable, this route can complete the film using working image and speech providers without repeated failed video submissions.
60
+
61
+ Normalize frame size, frame rate and pixel format before concatenation. Export a broadly playable H.264/AAC MP4, or the user's requested format. Check duration, dimensions, audio presence, frame count and representative rendered frames. Preserve truthful asset attribution: motion made with AnimaGuru remains generated footage even when exact typography and editing are composed locally.