@animaapp/cli 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { Command as Command21 } from "commander";
4
+ import { Command as Command24 } from "commander";
5
5
 
6
6
  // src/commands/create.ts
7
7
  import { Command } from "commander";
@@ -176,7 +176,19 @@ function validationError(message, detail) {
176
176
  );
177
177
  }
178
178
  function apiError(message, detail) {
179
- return new CliError("API_ERROR", message, EXIT_CODES.API_ERROR, detail);
179
+ const text = message.replace(/^Error: /, "");
180
+ return new CliError("API_ERROR", withWorkspaceHint(text), EXIT_CODES.API_ERROR, detail);
181
+ }
182
+ function withWorkspaceHint(message) {
183
+ if (/\bworkspaceId\b/.test(message)) {
184
+ return `${message}
185
+ Pass it with --workspace <id>; \`anima workspaces\` lists the ids.`;
186
+ }
187
+ if (message === "Workspace not found") {
188
+ return `${message}
189
+ \`anima workspaces\` lists the workspaces you can reach, with their ids.`;
190
+ }
191
+ return message;
180
192
  }
181
193
  function timeoutError() {
182
194
  return new CliError(
@@ -423,9 +435,16 @@ import { CallToolResultSchema, ErrorCode } from "@modelcontextprotocol/sdk/types
423
435
  import { createRequire } from "module";
424
436
  var { version } = createRequire(import.meta.url)("../package.json");
425
437
 
438
+ // src/clientIdentity.ts
439
+ var ANIMA_CLIENT_IDENTITY = {
440
+ type: "cli",
441
+ name: "anima-cli",
442
+ version
443
+ };
444
+
426
445
  // src/api/mcp.ts
427
446
  var MCP_PATH = "/v1/mcp";
428
- var CLIENT_INFO = { name: "anima-cli", version };
447
+ var CLIENT_INFO = { name: ANIMA_CLIENT_IDENTITY.name, version: ANIMA_CLIENT_IDENTITY.version };
429
448
  var DEFAULT_TIMEOUT_MS = 6e5;
430
449
  var AnimaMcpClient = class {
431
450
  #client;
@@ -433,7 +452,10 @@ var AnimaMcpClient = class {
433
452
  #url;
434
453
  constructor(config) {
435
454
  const headers = {
436
- Authorization: `Bearer ${config.token}`
455
+ Authorization: `Bearer ${config.token}`,
456
+ "X-Anima-Client-Type": ANIMA_CLIENT_IDENTITY.type,
457
+ "X-Anima-Client-Name": ANIMA_CLIENT_IDENTITY.name,
458
+ "X-Anima-Client-Version": ANIMA_CLIENT_IDENTITY.version
437
459
  };
438
460
  if (config.teamId) headers["X-Team-Id"] = config.teamId;
439
461
  if (config.figmaToken) headers["X-Figma-Token"] = config.figmaToken;
@@ -475,6 +497,11 @@ var AnimaMcpClient = class {
475
497
  if (err?.code === ErrorCode.RequestTimeout) {
476
498
  throw timeoutError();
477
499
  }
500
+ if (err?.code === ErrorCode.InvalidParams && /\bnot found\b/.test(err.message)) {
501
+ throw apiError(
502
+ `${name} is not available to this credential. An agent is offered it only once a human has granted it the access it needs in a workspace of this team (read to list, write to create).`
503
+ );
504
+ }
478
505
  throw err;
479
506
  }
480
507
  logEvent("mcp_tool_result", { tool: name, isError: result.isError ?? false });
@@ -827,6 +854,7 @@ async function createAnonymousArtifact(request, fetchFn = fetch) {
827
854
  const url = `${request.apiUrl.replace(/\/$/, "")}/v1/generationSessions`;
828
855
  const body = {
829
856
  type: "import",
857
+ clientType: ANIMA_CLIENT_IDENTITY.type,
830
858
  ...request.files ? { files: request.files } : {},
831
859
  ...request.zipUploadId ? { zipUploadId: request.zipUploadId } : {},
832
860
  ...request.framework ? { framework: request.framework } : {},
@@ -867,6 +895,14 @@ async function getAnonymousZipUploadUrl(apiUrl, fetchFn = fetch) {
867
895
  return { zipUploadId: payload.zipUploadId, uploadUrl: payload.uploadUrl };
868
896
  }
869
897
 
898
+ // src/utils/workspaceFlag.ts
899
+ function workspaceFlag(value) {
900
+ if (value !== void 0 && value.trim() === "") {
901
+ throw validationError("--workspace needs a workspace id; `anima workspaces` lists them");
902
+ }
903
+ return value;
904
+ }
905
+
870
906
  // src/commands/create.ts
871
907
  var createCommand = new Command("create").summary("Create an artifact: your code, an empty repo, or AI generation").description(
872
908
  "Create an artifact from your own code: empty for code you'll push (instant) or import your existing code (instant). Also supports AI generation: p2c/l2c/f2c (3-7 min)."
@@ -879,7 +915,10 @@ var createCommand = new Command("create").summary("Create an artifact: your code
879
915
  ).option("--styling <style>", "tailwind | css | plain_css | css_modules | inline_styles", "tailwind").option("--language <lang>", "typescript | javascript").option("--ui-library <lib>", "mui | antd | shadcn | clean_react").option("--guidelines <text>", "Additional constraints (p2c only)").option(
880
916
  "--artifact-type <kind>",
881
917
  "app | markdown \u2014 what the artifact IS. app (default) needs an index.html; markdown needs .md files"
882
- ).option("--name <name>", "Project name (empty/import only)").option("--from <path>", "Folder or .zip with your project (import only) \u2014 becomes the first commit").option(
918
+ ).option("--name <name>", "Project name (empty/import only)").option(
919
+ "-w, --workspace <id>",
920
+ "The workspace to create it in (id from `anima workspaces`). Optional when you can create in only one workspace; otherwise required, since there is no default"
921
+ ).option("--from <path>", "Folder or .zip with your project (import only) \u2014 becomes the first commit").option(
883
922
  "--anonymous",
884
923
  "Create without an account (import only). Returns a claim link to send a human, and a handoff token to log in with once they claim it"
885
924
  ).option(
@@ -946,7 +985,7 @@ ${grant.nextSteps}` : message, grant);
946
985
  return { created, status: status2 };
947
986
  }).then(({ created, status: status2 }) => ({ result: created, settled: status2 }));
948
987
  if (!result.success || !result.sessionId) {
949
- const message = result.reason || result.error || "Generation failed";
988
+ const message = result.message || result.reason || result.error || "Generation failed";
950
989
  throw apiError(result.nextSteps ? `${message}
951
990
  ${result.nextSteps}` : message, result);
952
991
  }
@@ -1152,6 +1191,12 @@ Log in first for the other types: anima login`
1152
1191
  if (type !== "import" && opts.from) {
1153
1192
  throw validationError("--from is only valid with -t import");
1154
1193
  }
1194
+ workspaceFlag(opts.workspace);
1195
+ if (opts.workspace !== void 0 && opts.anonymous) {
1196
+ throw validationError(
1197
+ "--workspace does not apply to --anonymous creates \u2014 they belong to no team until a human claims them"
1198
+ );
1199
+ }
1155
1200
  }
1156
1201
  function resolveFigmaToken(opts, stored) {
1157
1202
  const figmaToken = opts.figmaToken || process.env.FIGMA_TOKEN || stored;
@@ -1170,7 +1215,8 @@ function buildCreateArgs(type, opts) {
1170
1215
  // import is what made markdown artifacts render as empty apps.
1171
1216
  ...opts.framework && opts.artifactType !== "markdown" ? { framework: opts.framework } : {},
1172
1217
  ...opts.artifactType ? { artifactType: opts.artifactType } : {},
1173
- ...opts.name ? { name: opts.name } : {}
1218
+ ...opts.name ? { name: opts.name } : {},
1219
+ ...opts.workspace !== void 0 ? { workspaceId: opts.workspace } : {}
1174
1220
  };
1175
1221
  }
1176
1222
  const framework = opts.framework ?? "html";
@@ -1185,6 +1231,7 @@ function buildCreateArgs(type, opts) {
1185
1231
  };
1186
1232
  if (language) args.language = language;
1187
1233
  if (opts.uiLibrary) args.uiLibrary = opts.uiLibrary;
1234
+ if (opts.workspace !== void 0) args.workspaceId = opts.workspace;
1188
1235
  switch (type) {
1189
1236
  case "p2c":
1190
1237
  args.prompt = opts.prompt;
@@ -1209,22 +1256,107 @@ function buildCreateArgs(type, opts) {
1209
1256
  return args;
1210
1257
  }
1211
1258
 
1212
- // src/commands/list.ts
1259
+ // src/commands/createKnowledge.ts
1213
1260
  import { Command as Command2 } from "commander";
1261
+
1262
+ // src/commands/runner.ts
1263
+ async function withCommandErrors(mode, fn) {
1264
+ try {
1265
+ await fn();
1266
+ } catch (err) {
1267
+ if (err instanceof CliError) {
1268
+ if (mode === "pretty") {
1269
+ printError(err.message);
1270
+ } else {
1271
+ outputError(mode, { code: err.code, message: err.message });
1272
+ }
1273
+ process.exit(err.exitCode);
1274
+ }
1275
+ throw err;
1276
+ }
1277
+ }
1278
+
1279
+ // src/commands/createKnowledge.ts
1280
+ function buildCreateKnowledgeArgs(name, workspaceId) {
1281
+ if (name !== void 0 && name.length > 120) {
1282
+ throw validationError("--name must be at most 120 characters");
1283
+ }
1284
+ return {
1285
+ ...name === void 0 ? {} : { name },
1286
+ ...workspaceFlag(workspaceId) === void 0 ? {} : { workspaceId }
1287
+ };
1288
+ }
1289
+ function makeCreateKnowledgeCommand() {
1290
+ return new Command2("create-knowledge").summary("Create a knowledge artifact from Anima's template").description(
1291
+ "Create a ready knowledge artifact from Anima's server-provided template. The template supplies the files and framework; only an optional display name is accepted."
1292
+ ).option("--name <name>", 'Display name (defaults to "Untitled project"; max 120 characters)').option(
1293
+ "-w, --workspace <id>",
1294
+ "The workspace to create it in (id from `anima workspaces`). Optional when you can create in only one workspace; otherwise required, since there is no default"
1295
+ ).option("--json", "Output as JSON").option("--verbose", "Show progress on stderr").option("--api-url <url>", "API base URL (overrides config / ANIMA_API_URL)").option("--log-file <path>", "Append a JSON debug log of each step to this file").action(async (opts) => {
1296
+ const mode = detectOutputMode(opts);
1297
+ initLogger(opts.logFile);
1298
+ const progress = new ProgressReporter(mode, opts.verbose);
1299
+ await withCommandErrors(mode, async () => {
1300
+ try {
1301
+ await run();
1302
+ } catch (err) {
1303
+ progress.fail("Failed");
1304
+ throw err;
1305
+ }
1306
+ });
1307
+ async function run() {
1308
+ const args = buildCreateKnowledgeArgs(opts.name, opts.workspace);
1309
+ const auth = await resolveAuth(opts.apiUrl);
1310
+ progress.start("Creating knowledge artifact from template...");
1311
+ const result = await withMcp(
1312
+ { apiBaseUrl: resolveApiUrl(opts.apiUrl), token: auth.token, teamId: auth.teamId },
1313
+ (client) => client.callTool("artifact-create_knowledge", args)
1314
+ );
1315
+ if (!result.success || !result.sessionId) {
1316
+ throw apiError(result.message || result.reason || result.error || "Knowledge artifact creation failed", result);
1317
+ }
1318
+ progress.succeed("Knowledge artifact created");
1319
+ if (mode === "pretty") {
1320
+ printSessionResult({
1321
+ sessionId: result.sessionId,
1322
+ artifactUrl: result.artifactUrl,
1323
+ playgroundUrl: result.playgroundUrl
1324
+ });
1325
+ if (result.fileCount !== void 0) {
1326
+ printKeyValue("Template files", String(result.fileCount));
1327
+ }
1328
+ console.log();
1329
+ } else {
1330
+ outputResult(mode, result);
1331
+ }
1332
+ }
1333
+ });
1334
+ }
1335
+ var createKnowledgeCommand = makeCreateKnowledgeCommand();
1336
+
1337
+ // src/commands/list.ts
1338
+ import { Command as Command3 } from "commander";
1214
1339
  import chalk3 from "chalk";
1215
- var listCommand = new Command2("list").summary("List your team's artifacts, most-recently-updated first").aliases(["ls", "artifacts"]).description(
1216
- "List your team's artifacts (most-recently-updated first) so you can resume existing work."
1340
+ var listCommand = new Command3("list").summary("List the artifacts you can read, most-recently-updated first").aliases(["ls", "artifacts"]).description(
1341
+ "List the artifacts you can read, across every workspace you can reach or in one (most-recently-updated first), so you can resume existing work."
1342
+ ).option(
1343
+ "-w, --workspace <id>",
1344
+ "Only this workspace (id from `anima workspaces`); omit to span every workspace you can read"
1217
1345
  ).option("--json", "Output as JSON").option("--api-url <url>", "API base URL (overrides config / ANIMA_API_URL)").option("--log-file <path>", "Append a JSON debug log of each step to this file").action(async (opts) => {
1218
1346
  const mode = detectOutputMode(opts);
1219
1347
  initLogger(opts.logFile);
1220
1348
  try {
1221
1349
  const auth = await resolveAuth(opts.apiUrl);
1350
+ const workspaceId = workspaceFlag(opts.workspace);
1222
1351
  const result = await withMcp(
1223
1352
  { apiBaseUrl: resolveApiUrl(opts.apiUrl), token: auth.token, teamId: auth.teamId },
1224
- (client) => client.callTool("workspace-list_artifacts", {})
1353
+ (client) => client.callTool(
1354
+ "workspace-list_artifacts",
1355
+ workspaceId !== void 0 ? { workspaceId } : {}
1356
+ )
1225
1357
  );
1226
1358
  if (!result.success) {
1227
- const message = result.reason || result.error || "Could not list artifacts";
1359
+ const message = result.message || result.reason || result.error || "Could not list artifacts";
1228
1360
  throw apiError(message, result);
1229
1361
  }
1230
1362
  const artifacts = result.artifacts ?? [];
@@ -1233,20 +1365,19 @@ var listCommand = new Command2("list").summary("List your team's artifacts, most
1233
1365
  if (artifacts.length === 0) {
1234
1366
  console.log();
1235
1367
  console.log(
1236
- " No artifacts yet \u2014 create one with `create -t empty` or `create -t import`."
1368
+ workspaceId !== void 0 ? " Nothing you can read in that workspace." : " No artifacts you can read \u2014 create one with `create -t empty` or `create -t import`."
1237
1369
  );
1238
1370
  console.log();
1239
1371
  } else {
1240
1372
  console.log();
1241
1373
  for (const a of artifacts) {
1242
1374
  console.log(` ${chalk3.bold(a.name)} ${chalk3.gray(`(${a.type})`)}`);
1243
- if (a.sessionId) {
1244
- console.log(` ${chalk3.gray("session:")} ${a.sessionId}`);
1375
+ console.log(` ${chalk3.gray("session:")} ${a.sessionId}`);
1376
+ if (a.workspaceName) {
1377
+ console.log(` ${chalk3.gray("workspace:")} ${a.workspaceName}`);
1245
1378
  }
1246
1379
  console.log(` ${chalk3.gray("updated:")} ${formatUpdatedAt(a.updatedAt)}`);
1247
- if (a.sessionId) {
1248
- console.log(` ${chalk3.cyan.underline(artifactUrlFor(a.sessionId))}`);
1249
- }
1380
+ console.log(` ${chalk3.cyan.underline(artifactUrlFor(a.sessionId))}`);
1250
1381
  console.log();
1251
1382
  }
1252
1383
  if (result.truncated) {
@@ -1259,13 +1390,9 @@ var listCommand = new Command2("list").summary("List your team's artifacts, most
1259
1390
  } else {
1260
1391
  outputResult(mode, {
1261
1392
  success: true,
1262
- workspaceId: result.workspaceId,
1263
1393
  // The listing tool returns no URL; deriving it here saves every caller
1264
1394
  // the same string concatenation, and keeps one definition of the link.
1265
- artifacts: artifacts.map((a) => ({
1266
- ...a,
1267
- ...a.sessionId ? { artifactUrl: artifactUrlFor(a.sessionId) } : {}
1268
- })),
1395
+ artifacts: artifacts.map((a) => ({ ...a, artifactUrl: artifactUrlFor(a.sessionId) })),
1269
1396
  truncated: result.truncated ?? false
1270
1397
  });
1271
1398
  }
@@ -1289,10 +1416,65 @@ function formatUpdatedAt(iso) {
1289
1416
  return `${relative2} (${iso})`;
1290
1417
  }
1291
1418
 
1292
- // src/commands/status.ts
1293
- import { Command as Command3 } from "commander";
1419
+ // src/commands/workspaces.ts
1420
+ import { Command as Command4 } from "commander";
1294
1421
  import chalk4 from "chalk";
1295
- var statusCommand = new Command3("status").summary("Check whether an artifact finished generating").description(
1422
+ var workspacesCommand = new Command4("workspaces").summary("List the workspaces you can reach, and what you may do in each").aliases(["ws"]).description(
1423
+ "List the workspaces you can read in your team, with the capabilities you hold in each. Use a workspace id with `list --workspace` or `create --workspace`."
1424
+ ).option("--json", "Output as JSON").option("--api-url <url>", "API base URL (overrides config / ANIMA_API_URL)").option("--log-file <path>", "Append a JSON debug log of each step to this file").action(async (opts) => {
1425
+ const mode = detectOutputMode(opts);
1426
+ initLogger(opts.logFile);
1427
+ try {
1428
+ const auth = await resolveAuth(opts.apiUrl);
1429
+ const result = await withMcp(
1430
+ { apiBaseUrl: resolveApiUrl(opts.apiUrl), token: auth.token, teamId: auth.teamId },
1431
+ (client) => client.callTool("workspace-list_workspaces", {})
1432
+ );
1433
+ if (!result.success) {
1434
+ const message = result.message || result.reason || result.error || "Could not list workspaces";
1435
+ throw apiError(message, result);
1436
+ }
1437
+ const workspaces = result.workspaces ?? [];
1438
+ logEvent("workspaces_listed", { count: workspaces.length });
1439
+ if (mode === "pretty") {
1440
+ console.log();
1441
+ if (workspaces.length === 0) {
1442
+ console.log(" You cannot reach any workspace in this team.");
1443
+ console.log(
1444
+ chalk4.gray(" Someone who manages the team can grant you access to one.")
1445
+ );
1446
+ console.log();
1447
+ } else {
1448
+ for (const workspace of workspaces) {
1449
+ const label = workspace.isGeneral && workspace.name !== "General" ? chalk4.gray(" (General)") : "";
1450
+ console.log(` ${chalk4.bold(workspace.name)}${label}`);
1451
+ console.log(` ${chalk4.gray("id:")} ${workspace.workspaceId}`);
1452
+ console.log(
1453
+ ` ${chalk4.gray("you can:")} ${workspace.capabilities.join(", ") || chalk4.gray("nothing")}`
1454
+ );
1455
+ console.log();
1456
+ }
1457
+ }
1458
+ } else {
1459
+ outputResult(mode, { success: true, workspaces });
1460
+ }
1461
+ } catch (err) {
1462
+ if (err instanceof CliError) {
1463
+ if (mode === "pretty") {
1464
+ console.error(err.message);
1465
+ } else {
1466
+ outputError(mode, { code: err.code, message: err.message });
1467
+ }
1468
+ process.exit(err.exitCode);
1469
+ }
1470
+ throw err;
1471
+ }
1472
+ });
1473
+
1474
+ // src/commands/status.ts
1475
+ import { Command as Command5 } from "commander";
1476
+ import chalk5 from "chalk";
1477
+ var statusCommand = new Command5("status").summary("Check whether an artifact finished generating").description(
1296
1478
  "Check whether an artifact finished generating. --wait blocks until it is ready or failed."
1297
1479
  ).argument("<artifactUrl>", "Artifact URL or session ID").option("--wait", "Block until generation finishes instead of returning a snapshot").option("--json", "Output as JSON").option("--timeout <ms>", "How long --wait may block", "600000").option("--verbose", "Show progress on stderr").option("--api-url <url>", "API base URL (overrides config / ANIMA_API_URL)").option("--log-file <path>", "Append a JSON debug log of each step to this file").action(async (artifactUrl, opts) => {
1298
1480
  const mode = detectOutputMode(opts);
@@ -1348,10 +1530,10 @@ function printArtifactStatus(sessionId, result) {
1348
1530
  printKeyValue("Progress", `${result.progress}%`);
1349
1531
  }
1350
1532
  if (result.artifactUrl) {
1351
- printKeyValue("Your artifact", chalk4.cyan.underline(result.artifactUrl));
1533
+ printKeyValue("Your artifact", chalk5.cyan.underline(result.artifactUrl));
1352
1534
  }
1353
1535
  if (result.playgroundUrl) {
1354
- printKeyValue("Edit in Playground", chalk4.cyan.underline(result.playgroundUrl));
1536
+ printKeyValue("Edit in Playground", chalk5.cyan.underline(result.playgroundUrl));
1355
1537
  }
1356
1538
  if (result.nextStep) {
1357
1539
  console.log();
@@ -1361,8 +1543,8 @@ function printArtifactStatus(sessionId, result) {
1361
1543
  }
1362
1544
 
1363
1545
  // src/commands/publish.ts
1364
- import { Command as Command4 } from "commander";
1365
- var publishCommand = new Command4("publish").summary("Deploy to a public live URL (only when asked)").description("Deploy an artifact's app to a live public URL (1-3 min)").argument("<sessionId>", "Session ID from create command").option("--json", "Output as JSON").option("--verbose", "Show progress on stderr").option("--api-url <url>", "API base URL (overrides config / ANIMA_API_URL)").option("--log-file <path>", "Append a JSON debug log of each step to this file").action(async (sessionId, opts) => {
1546
+ import { Command as Command6 } from "commander";
1547
+ var publishCommand = new Command6("publish").summary("Deploy to a public live URL (only when asked)").description("Deploy an artifact's app to a live public URL (1-3 min)").argument("<sessionId>", "Session ID from create command").option("--json", "Output as JSON").option("--verbose", "Show progress on stderr").option("--api-url <url>", "API base URL (overrides config / ANIMA_API_URL)").option("--log-file <path>", "Append a JSON debug log of each step to this file").action(async (sessionId, opts) => {
1366
1548
  const mode = detectOutputMode(opts);
1367
1549
  initLogger(opts.logFile);
1368
1550
  const progress = new ProgressReporter(mode, opts.verbose);
@@ -1408,7 +1590,7 @@ var publishCommand = new Command4("publish").summary("Deploy to a public live UR
1408
1590
  });
1409
1591
 
1410
1592
  // src/commands/codegen.ts
1411
- import { Command as Command5 } from "commander";
1593
+ import { Command as Command7 } from "commander";
1412
1594
 
1413
1595
  // src/utils/files.ts
1414
1596
  import fs4 from "fs";
@@ -1436,7 +1618,7 @@ function writeFilesToDisk(files, outputDir) {
1436
1618
  }
1437
1619
 
1438
1620
  // src/commands/codegen.ts
1439
- var codegenCommand = new Command5("codegen").summary("Convert a Figma design to local code files").description("Convert Figma design to code files (no artifact)").requiredOption("--file-key <key>", "Figma file key or URL").option("--nodes <nodes>", "Figma node IDs, comma-separated").option("--figma-token <token>", "Figma personal access token (or set FIGMA_TOKEN env var)").option("-o, --output <dir>", "Output directory", "./generated").option("--framework <fw>", "react | html", "react").option("--styling <style>", "tailwind | plain_css", "tailwind").option("--language <lang>", "typescript | javascript").option("--ui-library <lib>", "mui | antd | shadcn | clean_react").option("--json", "Output as JSON").option("--timeout <ms>", "Timeout in milliseconds", "600000").option("--verbose", "Show progress on stderr").option("--api-url <url>", "API base URL (overrides config / ANIMA_API_URL)").option("--log-file <path>", "Append a JSON debug log of each step to this file").action(async (opts) => {
1621
+ var codegenCommand = new Command7("codegen").summary("Convert a Figma design to local code files").description("Convert Figma design to code files (no artifact)").requiredOption("--file-key <key>", "Figma file key or URL").option("--nodes <nodes>", "Figma node IDs, comma-separated").option("--figma-token <token>", "Figma personal access token (or set FIGMA_TOKEN env var)").option("-o, --output <dir>", "Output directory", "./generated").option("--framework <fw>", "react | html", "react").option("--styling <style>", "tailwind | plain_css", "tailwind").option("--language <lang>", "typescript | javascript").option("--ui-library <lib>", "mui | antd | shadcn | clean_react").option("--json", "Output as JSON").option("--timeout <ms>", "Timeout in milliseconds", "600000").option("--verbose", "Show progress on stderr").option("--api-url <url>", "API base URL (overrides config / ANIMA_API_URL)").option("--log-file <path>", "Append a JSON debug log of each step to this file").action(async (opts) => {
1440
1622
  const mode = detectOutputMode(opts);
1441
1623
  initLogger(opts.logFile);
1442
1624
  const progress = new ProgressReporter(mode, opts.verbose);
@@ -1508,8 +1690,8 @@ var codegenCommand = new Command5("codegen").summary("Convert a Figma design to
1508
1690
  });
1509
1691
 
1510
1692
  // src/commands/getGitToken.ts
1511
- import { Command as Command6 } from "commander";
1512
- import chalk5 from "chalk";
1693
+ import { Command as Command8 } from "commander";
1694
+ import chalk6 from "chalk";
1513
1695
 
1514
1696
  // src/utils/gitToken.ts
1515
1697
  function preview(payload) {
@@ -1555,7 +1737,7 @@ function gitConfigCommands(commitAuthor) {
1555
1737
  }
1556
1738
 
1557
1739
  // src/commands/getGitToken.ts
1558
- var getGitTokenCommand = new Command6("get-git-token").summary("Mint git access to an artifact's repository").description("Mint a short-lived git remote URL for an artifact (clone/push with git)").argument("<artifactUrl>", "Artifact URL or session ID").option("--json", "Output as JSON").option("--verbose", "Show progress on stderr").option("--api-url <url>", "API base URL (overrides config / ANIMA_API_URL)").option("--log-file <path>", "Append a JSON debug log of each step to this file").action(async (artifactUrl, opts) => {
1740
+ var getGitTokenCommand = new Command8("get-git-token").summary("Mint git access to an artifact's repository").description("Mint a short-lived git remote URL for an artifact (clone/push with git)").argument("<artifactUrl>", "Artifact URL or session ID").option("--json", "Output as JSON").option("--verbose", "Show progress on stderr").option("--api-url <url>", "API base URL (overrides config / ANIMA_API_URL)").option("--log-file <path>", "Append a JSON debug log of each step to this file").action(async (artifactUrl, opts) => {
1559
1741
  const mode = detectOutputMode(opts);
1560
1742
  initLogger(opts.logFile);
1561
1743
  const progress = new ProgressReporter(mode, opts.verbose);
@@ -1572,12 +1754,12 @@ var getGitTokenCommand = new Command6("get-git-token").summary("Mint git access
1572
1754
  progress.succeed(`Git access granted (${grant.access === "rw" ? "read-write" : "read-only"})`);
1573
1755
  if (mode === "pretty") {
1574
1756
  console.log();
1575
- console.log(` ${chalk5.cyan(`git clone ${grant.gitRemoteUrl}`)}`);
1757
+ console.log(` ${chalk6.cyan(`git clone ${grant.gitRemoteUrl}`)}`);
1576
1758
  console.log();
1577
1759
  if (grant.access === "rw") {
1578
1760
  if (grant.commitAuthor) {
1579
1761
  console.log(" In the clone, set who your commits are from:");
1580
- console.log(` ${chalk5.cyan(gitConfigCommands(grant.commitAuthor))}`);
1762
+ console.log(` ${chalk6.cyan(gitConfigCommands(grant.commitAuthor))}`);
1581
1763
  console.log();
1582
1764
  }
1583
1765
  console.log(" Commit and push to update the live artifact.");
@@ -1587,7 +1769,7 @@ var getGitTokenCommand = new Command6("get-git-token").summary("Mint git access
1587
1769
  if (grant.expiresAt) {
1588
1770
  console.log(
1589
1771
  ` The URL embeds a token that expires at ${grant.expiresAt} and cannot be renewed;
1590
- re-run this command and ${chalk5.cyan("git remote set-url origin <new url>")} to keep pushing.`
1772
+ re-run this command and ${chalk6.cyan("git remote set-url origin <new url>")} to keep pushing.`
1591
1773
  );
1592
1774
  }
1593
1775
  console.log(" Treat the URL as a secret \u2014 it grants access to this artifact.");
@@ -1620,11 +1802,11 @@ var getGitTokenCommand = new Command6("get-git-token").summary("Mint git access
1620
1802
  });
1621
1803
 
1622
1804
  // src/commands/mcpConfig.ts
1623
- import { Command as Command8 } from "commander";
1805
+ import { Command as Command10 } from "commander";
1624
1806
 
1625
1807
  // src/commands/login.ts
1626
- import { Command as Command7 } from "commander";
1627
- import chalk6 from "chalk";
1808
+ import { Command as Command9 } from "commander";
1809
+ import chalk7 from "chalk";
1628
1810
  import open from "open";
1629
1811
 
1630
1812
  // src/auth/device.ts
@@ -1814,7 +1996,7 @@ function resolveDeadline(claimableUntil, now) {
1814
1996
  }
1815
1997
 
1816
1998
  // src/commands/login.ts
1817
- var loginCommand = new Command7("login").summary("Connect this machine as a scoped agent identity").description("Log in via the device flow, redeem an invite with --invite, or finish an anonymous create with --handoff").option("--invite <url-or-code>", "Redeem an invite link (\u2026/invite/<code>.md) or bare code \u2014 no approval step").option(
1999
+ var loginCommand = new Command9("login").summary("Connect this machine as a scoped agent identity").description("Log in via the device flow, redeem an invite with --invite, or finish an anonymous create with --handoff").option("--invite <url-or-code>", "Redeem an invite link (\u2026/invite/<code>.md) or bare code \u2014 no approval step").option(
1818
2000
  "--handoff [token]",
1819
2001
  "Log in with the handoff token from an anonymous create. Waits until a human claims the artifact. Defaults to your last one"
1820
2002
  ).option("--client-name <name>", "How this CLI appears on the consent screen", "AgentGrid CLI").option("--no-open", "Do not try to open the verification page in a browser").option("--json", "Output as JSON").option("--api-url <url>", "API base URL (overrides config / ANIMA_API_URL)").option("--log-file <path>", "Append a JSON debug log of each step to this file").option(
@@ -1884,9 +2066,9 @@ var loginCommand = new Command7("login").summary("Connect this machine as a scop
1884
2066
  console.log();
1885
2067
  printInfo("To connect, open the verification page and enter this code:");
1886
2068
  console.log();
1887
- console.log(` ${chalk6.cyan.underline(info.verificationUri)}`);
2069
+ console.log(` ${chalk7.cyan.underline(info.verificationUri)}`);
1888
2070
  console.log();
1889
- console.log(` ${chalk6.bold.white(info.userCode)}`);
2071
+ console.log(` ${chalk7.bold.white(info.userCode)}`);
1890
2072
  console.log();
1891
2073
  printInfo(`Waiting for approval\u2026 (code expires in ${Math.round(info.expiresInSeconds / 60)} minutes)`);
1892
2074
  } else {
@@ -1963,7 +2145,7 @@ async function runHandoffLogin(opts, mode) {
1963
2145
  printInfo("Waiting for a human to claim the artifact:");
1964
2146
  if (pending.artifactUrl) {
1965
2147
  console.log();
1966
- console.log(` ${chalk6.cyan.underline(pending.artifactUrl)}`);
2148
+ console.log(` ${chalk7.cyan.underline(pending.artifactUrl)}`);
1967
2149
  }
1968
2150
  console.log();
1969
2151
  printInfo(
@@ -2038,7 +2220,7 @@ function buildMcpConfig(apiUrl) {
2038
2220
  }
2039
2221
 
2040
2222
  // src/commands/mcpConfig.ts
2041
- var mcpConfigCommand = new Command8("mcp-config").summary("Print an MCP server config for your MCP client").description("Print an MCP server config for your MCP client").option("--json", "Output as JSON").option("--api-url <url>", "API base URL (overrides config / ANIMA_API_URL)").action(async (opts) => {
2223
+ var mcpConfigCommand = new Command10("mcp-config").summary("Print an MCP server config for your MCP client").description("Print an MCP server config for your MCP client").option("--json", "Output as JSON").option("--api-url <url>", "API base URL (overrides config / ANIMA_API_URL)").action(async (opts) => {
2042
2224
  const mode = detectOutputMode(opts);
2043
2225
  try {
2044
2226
  const mcpConfig = buildMcpConfig(resolveApiUrl(opts.apiUrl));
@@ -2065,8 +2247,8 @@ var mcpConfigCommand = new Command8("mcp-config").summary("Print an MCP server c
2065
2247
  });
2066
2248
 
2067
2249
  // src/commands/auth.ts
2068
- import { Command as Command9 } from "commander";
2069
- import chalk7 from "chalk";
2250
+ import { Command as Command11 } from "commander";
2251
+ import chalk8 from "chalk";
2070
2252
  var DEFAULT_API_URL2 = "https://api.agentgrid.io";
2071
2253
  function describePendingHandoff(pending, state) {
2072
2254
  return {
@@ -2084,10 +2266,10 @@ function printPendingHandoff(pending, state) {
2084
2266
  return;
2085
2267
  }
2086
2268
  printInfo(`Anonymous artifact ${pending.sessionId} is awaiting a claim (until ${pending.claimableUntil}):`);
2087
- console.log(` ${chalk7.cyan.underline(pending.artifactUrl)}`);
2088
- console.log(` Run ${chalk7.cyan("anima login --handoff")} to wait for it.`);
2269
+ console.log(` ${chalk8.cyan.underline(pending.artifactUrl)}`);
2270
+ console.log(` Run ${chalk8.cyan("anima login --handoff")} to wait for it.`);
2089
2271
  }
2090
- var authCommand = new Command9("auth").summary("Inspect or manage stored credentials").description("Manage authentication").option("--token <token>", "Store AgentGrid API token").option("--figma-token <token>", "Store Figma personal access token").option("--team-id <teamId>", "Team ID to associate with token").option("--status", "Show current auth status").option("--logout", "Clear stored credentials (alias for `anima logout`)").option("--json", "Output as JSON").option("--api-url <url>", "API base URL", DEFAULT_API_URL2).action(async (opts) => {
2272
+ var authCommand = new Command11("auth").summary("Inspect or manage stored credentials").description("Manage authentication").option("--token <token>", "Store AgentGrid API token").option("--figma-token <token>", "Store Figma personal access token").option("--team-id <teamId>", "Team ID to associate with token").option("--status", "Show current auth status").option("--logout", "Clear stored credentials (alias for `anima logout`)").option("--json", "Output as JSON").option("--api-url <url>", "API base URL", DEFAULT_API_URL2).action(async (opts) => {
2091
2273
  const mode = detectOutputMode(opts);
2092
2274
  try {
2093
2275
  if (opts.token || opts.figmaToken) {
@@ -2187,13 +2369,13 @@ var authCommand = new Command9("auth").summary("Inspect or manage stored credent
2187
2369
  if (mode === "pretty") {
2188
2370
  console.log();
2189
2371
  printInfo("Auth commands:");
2190
- console.log(` ${chalk7.cyan("anima login")} Log in (device flow \u2014 approve on any browser)`);
2191
- console.log(` ${chalk7.cyan("anima login --handoff")} Wait for a human to claim your anonymous artifact`);
2192
- console.log(` ${chalk7.cyan("anima logout")} Clear all credentials`);
2193
- console.log(` ${chalk7.cyan("anima auth --status")} Check auth status`);
2194
- console.log(` ${chalk7.cyan("anima auth --figma-token <T>")} Save Figma token (needed for codegen/f2c)`);
2372
+ console.log(` ${chalk8.cyan("anima login")} Log in (device flow \u2014 approve on any browser)`);
2373
+ console.log(` ${chalk8.cyan("anima login --handoff")} Wait for a human to claim your anonymous artifact`);
2374
+ console.log(` ${chalk8.cyan("anima logout")} Clear all credentials`);
2375
+ console.log(` ${chalk8.cyan("anima auth --status")} Check auth status`);
2376
+ console.log(` ${chalk8.cyan("anima auth --figma-token <T>")} Save Figma token (needed for codegen/f2c)`);
2195
2377
  console.log();
2196
- console.log(` Figma token: ${chalk7.cyan.underline("https://www.figma.com")} > Settings > Personal access tokens`);
2378
+ console.log(` Figma token: ${chalk8.cyan.underline("https://www.figma.com")} > Settings > Personal access tokens`);
2197
2379
  console.log();
2198
2380
  } else {
2199
2381
  outputResult(mode, {
@@ -2215,8 +2397,8 @@ var authCommand = new Command9("auth").summary("Inspect or manage stored credent
2215
2397
  });
2216
2398
 
2217
2399
  // src/commands/logout.ts
2218
- import { Command as Command10 } from "commander";
2219
- var logoutCommand = new Command10("logout").summary("Disconnect: revoke and clear every stored credential").description("Clear all stored credentials (AgentGrid + Figma tokens) from this machine").option("--json", "Output as JSON").option("--api-url <url>", "API base URL (overrides config / ANIMA_API_URL)").action(async (opts) => {
2400
+ import { Command as Command12 } from "commander";
2401
+ var logoutCommand = new Command12("logout").summary("Disconnect: revoke and clear every stored credential").description("Clear all stored credentials (AgentGrid + Figma tokens) from this machine").option("--json", "Output as JSON").option("--api-url <url>", "API base URL (overrides config / ANIMA_API_URL)").action(async (opts) => {
2220
2402
  const mode = detectOutputMode(opts);
2221
2403
  const revoked = await revokeStoredRefreshToken(resolveApiUrl(opts.apiUrl));
2222
2404
  const hadCredentials = clearStorage();
@@ -2234,8 +2416,8 @@ var logoutCommand = new Command10("logout").summary("Disconnect: revoke and clea
2234
2416
  });
2235
2417
 
2236
2418
  // src/commands/unpublish.ts
2237
- import { Command as Command11 } from "commander";
2238
- var unpublishCommand = new Command11("unpublish").summary("Take a published artifact offline").description("Take a published artifact offline (clears its live URL; code and artifact stay intact)").argument("<sessionId>", "Session ID of the published artifact").option("--json", "Output as JSON").option("--verbose", "Show progress on stderr").option("--api-url <url>", "API base URL (overrides config / ANIMA_API_URL)").option("--log-file <path>", "Append a JSON debug log of each step to this file").action(async (sessionId, opts) => {
2419
+ import { Command as Command13 } from "commander";
2420
+ var unpublishCommand = new Command13("unpublish").summary("Take a published artifact offline").description("Take a published artifact offline (clears its live URL; code and artifact stay intact)").argument("<sessionId>", "Session ID of the published artifact").option("--json", "Output as JSON").option("--verbose", "Show progress on stderr").option("--api-url <url>", "API base URL (overrides config / ANIMA_API_URL)").option("--log-file <path>", "Append a JSON debug log of each step to this file").action(async (sessionId, opts) => {
2239
2421
  const mode = detectOutputMode(opts);
2240
2422
  initLogger(opts.logFile);
2241
2423
  const progress = new ProgressReporter(mode, opts.verbose);
@@ -2270,8 +2452,8 @@ var unpublishCommand = new Command11("unpublish").summary("Take a published arti
2270
2452
  });
2271
2453
 
2272
2454
  // src/commands/update.ts
2273
- import { Command as Command12 } from "commander";
2274
- var updateCommand = new Command12("update").summary("Rename an artifact or change its visibility").description("Update an artifact's metadata: display name and/or visibility (never touches code)").argument("<sessionId>", "Session ID of the artifact").option("--name <name>", "New display name").option("--privacy <privacy>", "public (anyone with the link) | private (team only)").option("--json", "Output as JSON").option("--verbose", "Show progress on stderr").option("--api-url <url>", "API base URL (overrides config / ANIMA_API_URL)").option("--log-file <path>", "Append a JSON debug log of each step to this file").action(async (sessionId, opts) => {
2455
+ import { Command as Command14 } from "commander";
2456
+ var updateCommand = new Command14("update").summary("Rename an artifact or change its visibility").description("Update an artifact's metadata: display name and/or visibility (never touches code)").argument("<sessionId>", "Session ID of the artifact").option("--name <name>", "New display name").option("--privacy <privacy>", "public (anyone with the link) | private (team only)").option("--json", "Output as JSON").option("--verbose", "Show progress on stderr").option("--api-url <url>", "API base URL (overrides config / ANIMA_API_URL)").option("--log-file <path>", "Append a JSON debug log of each step to this file").action(async (sessionId, opts) => {
2275
2457
  const mode = detectOutputMode(opts);
2276
2458
  initLogger(opts.logFile);
2277
2459
  const progress = new ProgressReporter(mode, opts.verbose);
@@ -2316,8 +2498,8 @@ var updateCommand = new Command12("update").summary("Rename an artifact or chang
2316
2498
  });
2317
2499
 
2318
2500
  // src/commands/config.ts
2319
- import { Command as Command13 } from "commander";
2320
- var configCommand = new Command13("config").summary("Store CLI preferences (e.g. a default API URL)").description(
2501
+ import { Command as Command15 } from "commander";
2502
+ var configCommand = new Command15("config").summary("Store CLI preferences (e.g. a default API URL)").description(
2321
2503
  `Store CLI preferences (keys: ${configKeys().join(", ")})`
2322
2504
  );
2323
2505
  configCommand.command("set <key> <value>").description("Set a config value").option("--json", "Output as JSON").action((key, value, opts) => {
@@ -2383,9 +2565,9 @@ function assertUrl(value) {
2383
2565
  }
2384
2566
 
2385
2567
  // src/commands/duplicate.ts
2386
- import { Command as Command14 } from "commander";
2387
- import chalk8 from "chalk";
2388
- function buildDuplicateArgs(artifactUrl, name) {
2568
+ import { Command as Command16 } from "commander";
2569
+ import chalk9 from "chalk";
2570
+ function buildDuplicateArgs(artifactUrl, name, workspaceId) {
2389
2571
  const sessionId = parseArtifactUrl(artifactUrl);
2390
2572
  if (!sessionId) {
2391
2573
  throw validationError("A source artifact URL or session ID is required");
@@ -2393,17 +2575,24 @@ function buildDuplicateArgs(artifactUrl, name) {
2393
2575
  if (name !== void 0 && (name.length === 0 || name.length > 120)) {
2394
2576
  throw validationError("--name must be between 1 and 120 characters");
2395
2577
  }
2396
- return { sessionId, ...name !== void 0 ? { name } : {} };
2578
+ return {
2579
+ sessionId,
2580
+ ...name !== void 0 ? { name } : {},
2581
+ ...workspaceFlag(workspaceId) !== void 0 ? { workspaceId } : {}
2582
+ };
2397
2583
  }
2398
2584
  function createDuplicateCommand() {
2399
- return new Command14("duplicate").summary("Copy an artifact into a new, independent one").description(
2585
+ return new Command16("duplicate").summary("Copy an artifact into a new, independent one").description(
2400
2586
  "Duplicate an artifact into a new, independent artifact. Copies code, assets, and supported database content; chat and custom domains are not copied. Duplication is non-idempotent: if a request times out or its response is lost, list recent artifacts before retrying because the first request may already have created the copy."
2401
- ).argument("<artifactUrl>", "Source artifact URL or session ID").option("--name <name>", "Display name for the duplicate (defaults to '<source name> (Copy)')").option("--json", "Output as JSON").option("--verbose", "Show progress on stderr").option("--api-url <url>", "API base URL (overrides config / ANIMA_API_URL)").option("--log-file <path>", "Append a JSON debug log of each step to this file").action(async (artifactUrl, opts) => {
2587
+ ).argument("<artifactUrl>", "Source artifact URL or session ID").option("--name <name>", "Display name for the duplicate (defaults to '<source name> (Copy)')").option(
2588
+ "-w, --workspace <id>",
2589
+ "The workspace to put the copy in (id from `anima workspaces`). Optional when you can create in only one workspace; otherwise required, since there is no default"
2590
+ ).option("--json", "Output as JSON").option("--verbose", "Show progress on stderr").option("--api-url <url>", "API base URL (overrides config / ANIMA_API_URL)").option("--log-file <path>", "Append a JSON debug log of each step to this file").action(async (artifactUrl, opts) => {
2402
2591
  const mode = detectOutputMode(opts);
2403
2592
  initLogger(opts.logFile);
2404
2593
  const progress = new ProgressReporter(mode, opts.verbose);
2405
2594
  try {
2406
- const args = buildDuplicateArgs(artifactUrl, opts.name);
2595
+ const args = buildDuplicateArgs(artifactUrl, opts.name, opts.workspace);
2407
2596
  const auth = await resolveAuth(opts.apiUrl);
2408
2597
  progress.start("Duplicating artifact...");
2409
2598
  const result = await withMcp(
@@ -2411,7 +2600,7 @@ function createDuplicateCommand() {
2411
2600
  (client) => client.callTool("artifact-duplicate", args)
2412
2601
  );
2413
2602
  if (!result.success) {
2414
- throw apiError(result.reason || result.error || "Artifact duplication failed", result);
2603
+ throw apiError(result.message || result.reason || result.error || "Artifact duplication failed", result);
2415
2604
  }
2416
2605
  progress.succeed("Artifact duplicated");
2417
2606
  if (mode === "pretty") {
@@ -2420,12 +2609,12 @@ function createDuplicateCommand() {
2420
2609
  printKeyValue("Name", result.name);
2421
2610
  printKeyValue("Session ID", result.sessionId);
2422
2611
  printKeyValue("Source session", result.sourceSessionId);
2423
- printKeyValue("Artifact", chalk8.cyan.underline(result.artifactUrl));
2612
+ printKeyValue("Artifact", chalk9.cyan.underline(result.artifactUrl));
2424
2613
  if (result.playgroundUrl) {
2425
- printKeyValue("Edit in Playground", chalk8.cyan.underline(result.playgroundUrl));
2614
+ printKeyValue("Edit in Playground", chalk9.cyan.underline(result.playgroundUrl));
2426
2615
  }
2427
2616
  if (result.previewUrl) {
2428
- printKeyValue("Preview", chalk8.cyan.underline(result.previewUrl));
2617
+ printKeyValue("Preview", chalk9.cyan.underline(result.previewUrl));
2429
2618
  }
2430
2619
  printKeyValue("Next steps", result.nextSteps);
2431
2620
  console.log();
@@ -2448,9 +2637,68 @@ function createDuplicateCommand() {
2448
2637
  }
2449
2638
  var duplicateCommand = createDuplicateCommand();
2450
2639
 
2640
+ // src/commands/move.ts
2641
+ import { Command as Command17 } from "commander";
2642
+ import chalk10 from "chalk";
2643
+ function buildMoveArgs(artifactUrl, workspaceId) {
2644
+ const sessionId = parseArtifactUrl(artifactUrl);
2645
+ if (!sessionId) {
2646
+ throw validationError("An artifact URL or session ID is required");
2647
+ }
2648
+ const destination = workspaceFlag(workspaceId);
2649
+ if (destination === void 0) {
2650
+ throw validationError("--workspace is required; `anima workspaces` lists the ids you can use");
2651
+ }
2652
+ return { sessionId, workspaceId: destination };
2653
+ }
2654
+ function createMoveCommand() {
2655
+ return new Command17("move").summary("File an artifact under a different workspace").description(
2656
+ "Move an artifact into another workspace of the same team. The artifact stays as it is: same git repository, session ID, URL, history and live deployment, so there is no new link to send. What changes is who can reach it, since a private artifact is reached through the workspace it is filed in: a move hands it to the people who reach the destination and takes it from the rest, including whoever you already sent the link to. Needs write on the artifact and write in the destination. Moving an artifact where it already is leaves everything as it was."
2657
+ ).argument("<artifactUrl>", "Artifact URL or session ID to move").option("-w, --workspace <id>", "The workspace to move it into (id from `anima workspaces`)").option("--json", "Output as JSON").option("--verbose", "Show progress on stderr").option("--api-url <url>", "API base URL (overrides config / ANIMA_API_URL)").option("--log-file <path>", "Append a JSON debug log of each step to this file").action(async (artifactUrl, opts) => {
2658
+ const mode = detectOutputMode(opts);
2659
+ initLogger(opts.logFile);
2660
+ const progress = new ProgressReporter(mode, opts.verbose);
2661
+ try {
2662
+ const args = buildMoveArgs(artifactUrl, opts.workspace);
2663
+ const auth = await resolveAuth(opts.apiUrl);
2664
+ progress.start("Moving artifact...");
2665
+ const result = await withMcp(
2666
+ { apiBaseUrl: resolveApiUrl(opts.apiUrl), token: auth.token, teamId: auth.teamId },
2667
+ (client) => client.callTool("workspace-move_artifact", args)
2668
+ );
2669
+ if (!result.success) {
2670
+ throw apiError(result.message || result.reason || result.error || "Could not move the artifact", result);
2671
+ }
2672
+ progress.succeed("Artifact moved");
2673
+ if (mode === "pretty") {
2674
+ console.log();
2675
+ printSuccess(`Artifact moved to ${result.workspaceName}`);
2676
+ printKeyValue("Session ID", result.sessionId);
2677
+ printKeyValue("Workspace", `${result.workspaceName} (${result.workspaceId})`);
2678
+ console.log(chalk10.gray(" The repository, URL and live deployment are unchanged."));
2679
+ console.log();
2680
+ } else {
2681
+ outputResult(mode, result);
2682
+ }
2683
+ } catch (err) {
2684
+ progress.fail("Failed");
2685
+ if (err instanceof CliError) {
2686
+ if (mode === "pretty") {
2687
+ printError(err.message);
2688
+ } else {
2689
+ outputError(mode, { code: err.code, message: err.message });
2690
+ }
2691
+ process.exit(err.exitCode);
2692
+ }
2693
+ throw err;
2694
+ }
2695
+ });
2696
+ }
2697
+ var moveCommand = createMoveCommand();
2698
+
2451
2699
  // src/commands/explore.ts
2452
- import { Command as Command15 } from "commander";
2453
- import chalk9 from "chalk";
2700
+ import { Command as Command18 } from "commander";
2701
+ import chalk11 from "chalk";
2454
2702
 
2455
2703
  // src/api/tool.ts
2456
2704
  async function runTool(toolName, args, options = {}) {
@@ -2476,25 +2724,8 @@ function describeFailure(result) {
2476
2724
  ${nextSteps}` : base;
2477
2725
  }
2478
2726
 
2479
- // src/commands/runner.ts
2480
- async function withCommandErrors(mode, fn) {
2481
- try {
2482
- await fn();
2483
- } catch (err) {
2484
- if (err instanceof CliError) {
2485
- if (mode === "pretty") {
2486
- printError(err.message);
2487
- } else {
2488
- outputError(mode, { code: err.code, message: err.message });
2489
- }
2490
- process.exit(err.exitCode);
2491
- }
2492
- throw err;
2493
- }
2494
- }
2495
-
2496
2727
  // src/commands/explore.ts
2497
- var exploreCommand = new Command15("explore").summary("Read an artifact's files: list, search, read, history").description(
2728
+ var exploreCommand = new Command18("explore").summary("Read an artifact's files: list, search, read, history").description(
2498
2729
  "Inspect an artifact's files without git: list them (--tree), search them (--search), read them (--read), or show commits (--history)."
2499
2730
  ).argument("<artifactUrl>", "Artifact URL or session ID").option("--tree", "List file paths").option("--search <query>", "Find text inside files \u2014 start here when you don't know the file").option("--read <paths...>", "Return the contents of these files").option("--history", "List commits, newest first").option("--path <prefix>", "tree/search: a literal directory prefix (not a glob)").option("--range <start,end>", "read: line window, 1-based inclusive, for a single file").option("--revision <rev>", "Look at the artifact as it was at this commit").option("--regex", "search: treat the query as a JavaScript regular expression").option("--case-sensitive", "search: match case exactly").option("--include-excluded", "tree/search: also look inside node_modules, dist and build").option("--limit <n>", "tree/search/history: maximum rows to return").option("--cursor <cursor>", "history: continue after a previous response's nextCursor").option("--json", "Output as JSON").option("--api-url <url>", "API base URL (overrides config / ANIMA_API_URL)").option("--log-file <path>", "Append a JSON debug log of each step to this file").action(async (artifactUrl, opts) => {
2500
2731
  const mode = detectOutputMode(opts);
@@ -2567,42 +2798,42 @@ function printExplore(action, result) {
2567
2798
  if (action === "tree") {
2568
2799
  for (const file of result.files ?? []) {
2569
2800
  const kind = result.unwritable?.[file];
2570
- console.log(` ${file}${kind ? chalk9.gray(` (${kind} \u2014 git only)`) : ""}`);
2801
+ console.log(` ${file}${kind ? chalk11.gray(` (${kind} \u2014 git only)`) : ""}`);
2571
2802
  }
2572
2803
  if (result.matchedFiles !== void 0) {
2573
2804
  console.log();
2574
- console.log(chalk9.gray(` ${(result.files ?? []).length} of ${result.matchedFiles} shown`));
2805
+ console.log(chalk11.gray(` ${(result.files ?? []).length} of ${result.matchedFiles} shown`));
2575
2806
  }
2576
2807
  } else if (action === "search") {
2577
2808
  for (const match of result.matches ?? []) {
2578
- console.log(` ${chalk9.cyan(match.path)}:${match.line} ${match.lineText.trim()}`);
2809
+ console.log(` ${chalk11.cyan(match.path)}:${match.line} ${match.lineText.trim()}`);
2579
2810
  }
2580
2811
  if (!result.matches?.length) {
2581
2812
  console.log(" No matches.");
2582
2813
  const skipped = result.notSearched;
2583
2814
  if (skipped?.excluded) {
2584
2815
  console.log(
2585
- chalk9.yellow(
2816
+ chalk11.yellow(
2586
2817
  ` ${skipped.excluded} files were skipped (node_modules, dist, build) \u2014 retry with --include-excluded.`
2587
2818
  )
2588
2819
  );
2589
2820
  }
2590
2821
  if (skipped?.assets) {
2591
- console.log(chalk9.gray(` ${skipped.assets} binary files cannot match a text query.`));
2822
+ console.log(chalk11.gray(` ${skipped.assets} binary files cannot match a text query.`));
2592
2823
  }
2593
2824
  }
2594
2825
  } else if (action === "read") {
2595
2826
  for (const file of result.files ?? []) {
2596
- console.log(chalk9.bold(` ${file.path}`));
2827
+ console.log(chalk11.bold(` ${file.path}`));
2597
2828
  if (file.asset) {
2598
- console.log(chalk9.gray(` binary \u2014 ${file.size ?? "?"} bytes, ${file.mime ?? "unknown type"}`));
2829
+ console.log(chalk11.gray(` binary \u2014 ${file.size ?? "?"} bytes, ${file.mime ?? "unknown type"}`));
2599
2830
  } else if (file.deferred) {
2600
- console.log(chalk9.yellow(" not returned: the response ran out of budget \u2014 read it on its own"));
2831
+ console.log(chalk11.yellow(" not returned: the response ran out of budget \u2014 read it on its own"));
2601
2832
  } else {
2602
2833
  console.log(file.content ?? "");
2603
2834
  if (file.truncated) {
2604
2835
  console.log(
2605
- chalk9.yellow(
2836
+ chalk11.yellow(
2606
2837
  ` lines ${file.startLine}-${file.endLine} of ${file.totalLines} \u2014 read the rest with --range`
2607
2838
  )
2608
2839
  );
@@ -2612,33 +2843,33 @@ function printExplore(action, result) {
2612
2843
  }
2613
2844
  for (const missing of result.missingPaths ?? []) {
2614
2845
  const near = result.suggestions?.[missing];
2615
- console.log(chalk9.yellow(` Not found: ${missing}${near?.length ? ` \u2014 did you mean ${near.join(", ")}?` : ""}`));
2846
+ console.log(chalk11.yellow(` Not found: ${missing}${near?.length ? ` \u2014 did you mean ${near.join(", ")}?` : ""}`));
2616
2847
  }
2617
2848
  } else {
2618
2849
  const commits = result.commit ? [result.commit] : result.commits ?? [];
2619
2850
  for (const commit of commits) {
2620
- console.log(` ${chalk9.yellow(commit.revision.slice(0, 10))} ${commit.message.split("\n")[0]}`);
2621
- console.log(chalk9.gray(` ${commit.author} \u2014 ${commit.date}`));
2851
+ console.log(` ${chalk11.yellow(commit.revision.slice(0, 10))} ${commit.message.split("\n")[0]}`);
2852
+ console.log(chalk11.gray(` ${commit.author} \u2014 ${commit.date}`));
2622
2853
  }
2623
2854
  for (const changed of result.commit?.changedFiles ?? []) {
2624
- console.log(chalk9.gray(` ${changed.change.padEnd(8)} ${changed.path}`));
2855
+ console.log(chalk11.gray(` ${changed.change.padEnd(8)} ${changed.path}`));
2625
2856
  }
2626
2857
  }
2627
2858
  if (result.truncated) {
2628
- console.log(chalk9.yellow(" The list was capped \u2014 raise --limit to see more."));
2859
+ console.log(chalk11.yellow(" The list was capped \u2014 raise --limit to see more."));
2629
2860
  }
2630
2861
  if (result.revision) {
2631
2862
  console.log();
2632
- console.log(chalk9.gray(` revision: ${result.revision} (pass to \`anima edit --base-revision\`)`));
2863
+ console.log(chalk11.gray(` revision: ${result.revision} (pass to \`anima edit --base-revision\`)`));
2633
2864
  }
2634
2865
  console.log();
2635
2866
  }
2636
2867
 
2637
2868
  // src/commands/edit.ts
2638
- import { Command as Command16 } from "commander";
2869
+ import { Command as Command19 } from "commander";
2639
2870
  import fs5 from "fs";
2640
- import chalk10 from "chalk";
2641
- var editCommand = new Command16("edit").summary("Change an artifact's files and commit them").description(
2871
+ import chalk12 from "chalk";
2872
+ var editCommand = new Command19("edit").summary("Change an artifact's files and commit them").description(
2642
2873
  "Change an artifact's files and commit them, without git. All changes land as one commit."
2643
2874
  ).argument("<artifactUrl>", "Artifact URL or session ID").requiredOption("-m, --message <message>", "Commit message").option(
2644
2875
  "--base-revision <rev>",
@@ -2660,7 +2891,7 @@ var editCommand = new Command16("edit").summary("Change an artifact's files and
2660
2891
  printSuccess("Committed.");
2661
2892
  for (const changed of result.changedFiles ?? []) {
2662
2893
  const detail = changed.op === "move" ? `${changed.fromPath} \u2192 ${changed.path}` : changed.replacements !== void 0 ? `${changed.path} (${changed.replacements} replaced)` : changed.path;
2663
- console.log(` ${chalk10.gray(changed.op.padEnd(12))} ${detail}`);
2894
+ console.log(` ${chalk12.gray(changed.op.padEnd(12))} ${detail}`);
2664
2895
  }
2665
2896
  console.log();
2666
2897
  printKeyValue("Revision", result.revision ?? "unknown");
@@ -2758,11 +2989,11 @@ function readFile(path7) {
2758
2989
  }
2759
2990
 
2760
2991
  // src/commands/uploadAsset.ts
2761
- import { Command as Command17 } from "commander";
2992
+ import { Command as Command20 } from "commander";
2762
2993
  import fs6 from "fs";
2763
2994
  import path5 from "path";
2764
- import chalk11 from "chalk";
2765
- var uploadAssetCommand = new Command17("upload-asset").summary("Add an image, font or media file over 256 KB").description(
2995
+ import chalk13 from "chalk";
2996
+ var uploadAssetCommand = new Command20("upload-asset").summary("Add an image, font or media file over 256 KB").description(
2766
2997
  "Add a large image, font or media file (over 256 KB) to an artifact: stage it, upload it, and commit it at --path."
2767
2998
  ).argument("<artifactUrl>", "Artifact URL or session ID").argument("<file>", "Local file to upload").option(
2768
2999
  "--path <path>",
@@ -2799,7 +3030,7 @@ var uploadAssetCommand = new Command17("upload-asset").summary("Add an image, fo
2799
3030
  console.log();
2800
3031
  console.log(" Commit it where it belongs with:");
2801
3032
  console.log(
2802
- ` ${chalk11.cyan(
3033
+ ` ${chalk13.cyan(
2803
3034
  `anima edit ${sessionId} -m "Add asset" --changes '[{"op":"write","path":"public/${path5.basename(file)}","assetUploadId":"${grant.assetUploadId}"}]'`
2804
3035
  )}`
2805
3036
  );
@@ -2876,8 +3107,8 @@ function readAsset(file) {
2876
3107
  }
2877
3108
 
2878
3109
  // src/commands/delete.ts
2879
- import { Command as Command18 } from "commander";
2880
- var deleteCommand = new Command18("delete").summary("Delete an artifact (reversible: hidden, not erased)").description("Soft-delete an artifact only when explicitly requested (unpublish it first; content is retained)").argument("<artifactUrl>", "Artifact URL or session ID to soft-delete").option("--json", "Output as JSON").option("--verbose", "Show progress on stderr").option("--api-url <url>", "API base URL (overrides config / ANIMA_API_URL)").option("--log-file <path>", "Append a JSON debug log of each step to this file").action(async (artifactUrl, opts) => {
3110
+ import { Command as Command21 } from "commander";
3111
+ var deleteCommand = new Command21("delete").summary("Delete an artifact (reversible: hidden, not erased)").description("Soft-delete an artifact only when explicitly requested (unpublish it first; content is retained)").argument("<artifactUrl>", "Artifact URL or session ID to soft-delete").option("--json", "Output as JSON").option("--verbose", "Show progress on stderr").option("--api-url <url>", "API base URL (overrides config / ANIMA_API_URL)").option("--log-file <path>", "Append a JSON debug log of each step to this file").action(async (artifactUrl, opts) => {
2881
3112
  const mode = detectOutputMode(opts);
2882
3113
  initLogger(opts.logFile);
2883
3114
  const progress = new ProgressReporter(mode, opts.verbose);
@@ -2918,9 +3149,9 @@ var deleteCommand = new Command18("delete").summary("Delete an artifact (reversi
2918
3149
  });
2919
3150
 
2920
3151
  // src/commands/review.ts
2921
- import { Command as Command19 } from "commander";
2922
- import chalk12 from "chalk";
2923
- var listSubcommand = new Command19("list").summary("Read the comments addressed to you").description("Read the review comments addressed to you, newest artifact activity first").argument("[artifactUrl]", "Limit to one artifact; omit for every review addressed to you").option("--limit <n>", "Maximum reviews to return (1-50, default 20)").option("--json", "Output as JSON").option("--api-url <url>", "API base URL (overrides config / ANIMA_API_URL)").option("--log-file <path>", "Append a JSON debug log of each step to this file").action(async (artifactUrl, opts) => {
3152
+ import { Command as Command22 } from "commander";
3153
+ import chalk14 from "chalk";
3154
+ var listSubcommand = new Command22("list").summary("Read the comments addressed to you").description("Read the review comments addressed to you, newest artifact activity first").argument("[artifactUrl]", "Limit to one artifact; omit for every review addressed to you").option("--limit <n>", "Maximum reviews to return (1-50, default 20)").option("--json", "Output as JSON").option("--api-url <url>", "API base URL (overrides config / ANIMA_API_URL)").option("--log-file <path>", "Append a JSON debug log of each step to this file").action(async (artifactUrl, opts) => {
2924
3155
  const mode = detectOutputMode(opts);
2925
3156
  initLogger(opts.logFile);
2926
3157
  await withCommandErrors(mode, async () => {
@@ -2935,7 +3166,7 @@ var listSubcommand = new Command19("list").summary("Read the comments addressed
2935
3166
  }
2936
3167
  });
2937
3168
  });
2938
- var resolveSubcommand = new Command19("resolve").summary("Close one comment without a commit").description("Close one comment without a commit \u2014 for a question answered, or a change you decided against").argument("<commentId>", "Comment to close, from `anima review list`").option("--note <text>", "What to tell the reviewer as the closing message").option("--json", "Output as JSON").option("--api-url <url>", "API base URL (overrides config / ANIMA_API_URL)").option("--log-file <path>", "Append a JSON debug log of each step to this file").action(async (commentId, opts) => {
3169
+ var resolveSubcommand = new Command22("resolve").summary("Close one comment without a commit").description("Close one comment without a commit \u2014 for a question answered, or a change you decided against").argument("<commentId>", "Comment to close, from `anima review list`").option("--note <text>", "What to tell the reviewer as the closing message").option("--json", "Output as JSON").option("--api-url <url>", "API base URL (overrides config / ANIMA_API_URL)").option("--log-file <path>", "Append a JSON debug log of each step to this file").action(async (commentId, opts) => {
2939
3170
  const mode = detectOutputMode(opts);
2940
3171
  initLogger(opts.logFile);
2941
3172
  await withCommandErrors(mode, async () => {
@@ -2954,7 +3185,7 @@ var resolveSubcommand = new Command19("resolve").summary("Close one comment with
2954
3185
  }
2955
3186
  });
2956
3187
  });
2957
- var replySubcommand = new Command19("reply").summary("Answer one comment, leaving it open").description("Say something back on one comment and LEAVE IT OPEN \u2014 to ask, report, or say you are blocked").argument("<commentId>", "Comment to reply to, from `anima review list`").requiredOption("-m, --message <message>", "What to say to the reviewer").option("--json", "Output as JSON").option("--api-url <url>", "API base URL (overrides config / ANIMA_API_URL)").option("--log-file <path>", "Append a JSON debug log of each step to this file").action(async (commentId, opts) => {
3188
+ var replySubcommand = new Command22("reply").summary("Answer one comment, leaving it open").description("Say something back on one comment and LEAVE IT OPEN \u2014 to ask, report, or say you are blocked").argument("<commentId>", "Comment to reply to, from `anima review list`").requiredOption("-m, --message <message>", "What to say to the reviewer").option("--json", "Output as JSON").option("--api-url <url>", "API base URL (overrides config / ANIMA_API_URL)").option("--log-file <path>", "Append a JSON debug log of each step to this file").action(async (commentId, opts) => {
2958
3189
  const mode = detectOutputMode(opts);
2959
3190
  initLogger(opts.logFile);
2960
3191
  await withCommandErrors(mode, async () => {
@@ -2973,7 +3204,7 @@ var replySubcommand = new Command19("reply").summary("Answer one comment, leavin
2973
3204
  }
2974
3205
  });
2975
3206
  });
2976
- var reviewCommand = new Command19("review").summary("Read and answer the comments humans addressed to you").description("Read and answer the review comments humans addressed to you (list / resolve / reply)").addCommand(listSubcommand).addCommand(resolveSubcommand).addCommand(replySubcommand);
3207
+ var reviewCommand = new Command22("review").summary("Read and answer the comments humans addressed to you").description("Read and answer the review comments humans addressed to you (list / resolve / reply)").addCommand(listSubcommand).addCommand(resolveSubcommand).addCommand(replySubcommand);
2977
3208
  function parseLimit2(value) {
2978
3209
  const limit = Number(value);
2979
3210
  if (!Number.isInteger(limit) || limit < 1 || limit > 50) {
@@ -2990,29 +3221,29 @@ function printReviews(result) {
2990
3221
  return;
2991
3222
  }
2992
3223
  for (const review of reviews) {
2993
- console.log(` ${chalk12.bold(`Review ${review.reviewId}`)} ${chalk12.gray(`by ${review.author.name}`)}`);
2994
- console.log(` ${chalk12.cyan.underline(review.artifactUrl)}`);
3224
+ console.log(` ${chalk14.bold(`Review ${review.reviewId}`)} ${chalk14.gray(`by ${review.author.name}`)}`);
3225
+ console.log(` ${chalk14.cyan.underline(review.artifactUrl)}`);
2995
3226
  if (!review.isLatest) {
2996
3227
  console.log(
2997
- chalk12.yellow(" The artifact moved since this was written \u2014 check the comments still apply.")
3228
+ chalk14.yellow(" The artifact moved since this was written \u2014 check the comments still apply.")
2998
3229
  );
2999
3230
  }
3000
3231
  console.log();
3001
3232
  for (const comment of review.comments) {
3002
3233
  const marks = [
3003
- comment.status !== "open" ? chalk12.gray(comment.status) : null,
3004
- comment.unassigned ? chalk12.yellow("unassigned") : null
3234
+ comment.status !== "open" ? chalk14.gray(comment.status) : null,
3235
+ comment.unassigned ? chalk14.yellow("unassigned") : null
3005
3236
  ].filter(Boolean).join(" ");
3006
- console.log(` ${chalk12.bold(`#${comment.index}`)} ${comment.body}${marks ? ` ${marks}` : ""}`);
3007
- console.log(chalk12.gray(` ${comment.commentId}${describeAnchor(comment)}`));
3237
+ console.log(` ${chalk14.bold(`#${comment.index}`)} ${comment.body}${marks ? ` ${marks}` : ""}`);
3238
+ console.log(chalk14.gray(` ${comment.commentId}${describeAnchor(comment)}`));
3008
3239
  for (const reply of comment.replies ?? []) {
3009
- console.log(chalk12.gray(` \u21B3 ${reply.author.name}: ${reply.body}`));
3240
+ console.log(chalk14.gray(` \u21B3 ${reply.author.name}: ${reply.body}`));
3010
3241
  }
3011
3242
  console.log();
3012
3243
  }
3013
3244
  printKeyValue("To close these in a commit", "");
3014
3245
  for (const line of review.resolveTrailer.split("\n")) {
3015
- console.log(` ${chalk12.gray(line)}`);
3246
+ console.log(` ${chalk14.gray(line)}`);
3016
3247
  }
3017
3248
  console.log();
3018
3249
  }
@@ -3033,13 +3264,13 @@ function describeAnchor(comment) {
3033
3264
  }
3034
3265
 
3035
3266
  // src/commands/skill.ts
3036
- import { Command as Command20 } from "commander";
3267
+ import { Command as Command23 } from "commander";
3037
3268
  import fs7 from "fs";
3038
3269
  import path6 from "path";
3039
- import chalk13 from "chalk";
3270
+ import chalk15 from "chalk";
3040
3271
  var GUIDE_PATH = "/guide.md";
3041
3272
  function createSkillCommand() {
3042
- return new Command20("skill").summary("Install the AgentGrid guide as a skill your agent can read").description(
3273
+ return new Command23("skill").summary("Install the AgentGrid guide as a skill your agent can read").description(
3043
3274
  "Fetch the AgentGrid agent guide and write it as a SKILL.md your runtime can load, or print it to stdout. The guide comes from the API, so it stays current; the command reference is generated from this CLI's own commands."
3044
3275
  ).argument(
3045
3276
  "[path]",
@@ -3066,8 +3297,8 @@ ${body}
3066
3297
  logEvent("skill_installed", { path: target, bytes: document.length });
3067
3298
  if (mode === "pretty") {
3068
3299
  console.log();
3069
- printSuccess(`Skill written to ${chalk13.cyan(target)}`);
3070
- console.log(` ${chalk13.gray(`${document.length} bytes, from ${apiUrl}${GUIDE_PATH}`)}`);
3300
+ printSuccess(`Skill written to ${chalk15.cyan(target)}`);
3301
+ console.log(` ${chalk15.gray(`${document.length} bytes, from ${apiUrl}${GUIDE_PATH}`)}`);
3071
3302
  console.log();
3072
3303
  } else {
3073
3304
  outputResult(mode, {
@@ -3196,7 +3427,7 @@ function writeDocument(target, document) {
3196
3427
  }
3197
3428
 
3198
3429
  // src/index.ts
3199
- var program = new Command21().name("anima").description(
3430
+ var program = new Command24().name("anima").description(
3200
3431
  `AgentGrid CLI \u2014 create, change, publish and share artifacts on AgentGrid.
3201
3432
  An artifact is a real git repository, and every command below runs one
3202
3433
  AgentGrid MCP tool for you. The command list is at the bottom; run
@@ -3206,6 +3437,7 @@ THE MAIN FLOW (build something and put it on AgentGrid):
3206
3437
  1. anima login # once; a human approves
3207
3438
  2. anima create -t import --from ./my-project # your code \u2192 a live app
3208
3439
  (or: create -t empty --framework html \u2014 an empty repo you push to)
3440
+ (can create in several workspaces? add --workspace <id>; see "workspaces")
3209
3441
  3. Change it. Two doors, same git repository:
3210
3442
  - no clone: "explore" to read the files, "edit" to commit them. Needs no
3211
3443
  shell, no git binary and no network of its own \u2014 prefer it.
@@ -3241,6 +3473,7 @@ EXAMPLES:
3241
3473
  anima explore <sessionId> --search "buttonColor"
3242
3474
  anima edit <sessionId> -m "Blue pill" --replace src/App.tsx --old red --new blue
3243
3475
  anima review list # what humans asked you to change
3476
+ anima move <sessionId> -w <workspaceId> # re-file it; the URL is unchanged
3244
3477
  anima publish <sessionId> # only on explicit request
3245
3478
 
3246
3479
  OUTPUT: pretty text in a terminal; a single JSON object on stdout when piped
@@ -3249,8 +3482,10 @@ OUTPUT: pretty text in a terminal; a single JSON object on stdout when piped
3249
3482
  ).version(version, "-v, --version");
3250
3483
  program.addCommand(loginCommand);
3251
3484
  program.addCommand(logoutCommand);
3485
+ program.addCommand(workspacesCommand);
3252
3486
  program.addCommand(listCommand);
3253
3487
  program.addCommand(createCommand);
3488
+ program.addCommand(createKnowledgeCommand);
3254
3489
  program.addCommand(statusCommand);
3255
3490
  program.addCommand(exploreCommand);
3256
3491
  program.addCommand(editCommand);
@@ -3258,6 +3493,7 @@ program.addCommand(uploadAssetCommand);
3258
3493
  program.addCommand(getGitTokenCommand);
3259
3494
  program.addCommand(reviewCommand);
3260
3495
  program.addCommand(duplicateCommand);
3496
+ program.addCommand(moveCommand);
3261
3497
  program.addCommand(publishCommand);
3262
3498
  program.addCommand(unpublishCommand);
3263
3499
  program.addCommand(updateCommand);