@animaapp/cli 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -6
- package/dist/index.js +317 -142
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
|
4
|
+
import { Command as Command23 } 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
|
-
|
|
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:
|
|
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(
|
|
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/
|
|
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
|
|
1216
|
-
"List
|
|
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(
|
|
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
|
|
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
|
-
|
|
1244
|
-
|
|
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
|
-
|
|
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/
|
|
1293
|
-
import { Command as
|
|
1419
|
+
// src/commands/workspaces.ts
|
|
1420
|
+
import { Command as Command4 } from "commander";
|
|
1294
1421
|
import chalk4 from "chalk";
|
|
1295
|
-
var
|
|
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",
|
|
1533
|
+
printKeyValue("Your artifact", chalk5.cyan.underline(result.artifactUrl));
|
|
1352
1534
|
}
|
|
1353
1535
|
if (result.playgroundUrl) {
|
|
1354
|
-
printKeyValue("Edit in Playground",
|
|
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
|
|
1365
|
-
var publishCommand = new
|
|
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
|
|
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
|
|
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
|
|
1512
|
-
import
|
|
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
|
|
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(` ${
|
|
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(` ${
|
|
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 ${
|
|
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
|
|
1805
|
+
import { Command as Command10 } from "commander";
|
|
1624
1806
|
|
|
1625
1807
|
// src/commands/login.ts
|
|
1626
|
-
import { Command as
|
|
1627
|
-
import
|
|
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
|
|
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(` ${
|
|
2069
|
+
console.log(` ${chalk7.cyan.underline(info.verificationUri)}`);
|
|
1888
2070
|
console.log();
|
|
1889
|
-
console.log(` ${
|
|
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(` ${
|
|
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
|
|
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
|
|
2069
|
-
import
|
|
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(` ${
|
|
2088
|
-
console.log(` Run ${
|
|
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
|
|
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(` ${
|
|
2191
|
-
console.log(` ${
|
|
2192
|
-
console.log(` ${
|
|
2193
|
-
console.log(` ${
|
|
2194
|
-
console.log(` ${
|
|
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: ${
|
|
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
|
|
2219
|
-
var logoutCommand = new
|
|
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
|
|
2238
|
-
var unpublishCommand = new
|
|
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
|
|
2274
|
-
var updateCommand = new
|
|
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
|
|
2320
|
-
var configCommand = new
|
|
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
|
|
2387
|
-
import
|
|
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 {
|
|
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
|
|
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(
|
|
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",
|
|
2612
|
+
printKeyValue("Artifact", chalk9.cyan.underline(result.artifactUrl));
|
|
2424
2613
|
if (result.playgroundUrl) {
|
|
2425
|
-
printKeyValue("Edit in Playground",
|
|
2614
|
+
printKeyValue("Edit in Playground", chalk9.cyan.underline(result.playgroundUrl));
|
|
2426
2615
|
}
|
|
2427
2616
|
if (result.previewUrl) {
|
|
2428
|
-
printKeyValue("Preview",
|
|
2617
|
+
printKeyValue("Preview", chalk9.cyan.underline(result.previewUrl));
|
|
2429
2618
|
}
|
|
2430
2619
|
printKeyValue("Next steps", result.nextSteps);
|
|
2431
2620
|
console.log();
|
|
@@ -2449,8 +2638,8 @@ function createDuplicateCommand() {
|
|
|
2449
2638
|
var duplicateCommand = createDuplicateCommand();
|
|
2450
2639
|
|
|
2451
2640
|
// src/commands/explore.ts
|
|
2452
|
-
import { Command as
|
|
2453
|
-
import
|
|
2641
|
+
import { Command as Command17 } from "commander";
|
|
2642
|
+
import chalk10 from "chalk";
|
|
2454
2643
|
|
|
2455
2644
|
// src/api/tool.ts
|
|
2456
2645
|
async function runTool(toolName, args, options = {}) {
|
|
@@ -2476,25 +2665,8 @@ function describeFailure(result) {
|
|
|
2476
2665
|
${nextSteps}` : base;
|
|
2477
2666
|
}
|
|
2478
2667
|
|
|
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
2668
|
// src/commands/explore.ts
|
|
2497
|
-
var exploreCommand = new
|
|
2669
|
+
var exploreCommand = new Command17("explore").summary("Read an artifact's files: list, search, read, history").description(
|
|
2498
2670
|
"Inspect an artifact's files without git: list them (--tree), search them (--search), read them (--read), or show commits (--history)."
|
|
2499
2671
|
).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
2672
|
const mode = detectOutputMode(opts);
|
|
@@ -2567,42 +2739,42 @@ function printExplore(action, result) {
|
|
|
2567
2739
|
if (action === "tree") {
|
|
2568
2740
|
for (const file of result.files ?? []) {
|
|
2569
2741
|
const kind = result.unwritable?.[file];
|
|
2570
|
-
console.log(` ${file}${kind ?
|
|
2742
|
+
console.log(` ${file}${kind ? chalk10.gray(` (${kind} \u2014 git only)`) : ""}`);
|
|
2571
2743
|
}
|
|
2572
2744
|
if (result.matchedFiles !== void 0) {
|
|
2573
2745
|
console.log();
|
|
2574
|
-
console.log(
|
|
2746
|
+
console.log(chalk10.gray(` ${(result.files ?? []).length} of ${result.matchedFiles} shown`));
|
|
2575
2747
|
}
|
|
2576
2748
|
} else if (action === "search") {
|
|
2577
2749
|
for (const match of result.matches ?? []) {
|
|
2578
|
-
console.log(` ${
|
|
2750
|
+
console.log(` ${chalk10.cyan(match.path)}:${match.line} ${match.lineText.trim()}`);
|
|
2579
2751
|
}
|
|
2580
2752
|
if (!result.matches?.length) {
|
|
2581
2753
|
console.log(" No matches.");
|
|
2582
2754
|
const skipped = result.notSearched;
|
|
2583
2755
|
if (skipped?.excluded) {
|
|
2584
2756
|
console.log(
|
|
2585
|
-
|
|
2757
|
+
chalk10.yellow(
|
|
2586
2758
|
` ${skipped.excluded} files were skipped (node_modules, dist, build) \u2014 retry with --include-excluded.`
|
|
2587
2759
|
)
|
|
2588
2760
|
);
|
|
2589
2761
|
}
|
|
2590
2762
|
if (skipped?.assets) {
|
|
2591
|
-
console.log(
|
|
2763
|
+
console.log(chalk10.gray(` ${skipped.assets} binary files cannot match a text query.`));
|
|
2592
2764
|
}
|
|
2593
2765
|
}
|
|
2594
2766
|
} else if (action === "read") {
|
|
2595
2767
|
for (const file of result.files ?? []) {
|
|
2596
|
-
console.log(
|
|
2768
|
+
console.log(chalk10.bold(` ${file.path}`));
|
|
2597
2769
|
if (file.asset) {
|
|
2598
|
-
console.log(
|
|
2770
|
+
console.log(chalk10.gray(` binary \u2014 ${file.size ?? "?"} bytes, ${file.mime ?? "unknown type"}`));
|
|
2599
2771
|
} else if (file.deferred) {
|
|
2600
|
-
console.log(
|
|
2772
|
+
console.log(chalk10.yellow(" not returned: the response ran out of budget \u2014 read it on its own"));
|
|
2601
2773
|
} else {
|
|
2602
2774
|
console.log(file.content ?? "");
|
|
2603
2775
|
if (file.truncated) {
|
|
2604
2776
|
console.log(
|
|
2605
|
-
|
|
2777
|
+
chalk10.yellow(
|
|
2606
2778
|
` lines ${file.startLine}-${file.endLine} of ${file.totalLines} \u2014 read the rest with --range`
|
|
2607
2779
|
)
|
|
2608
2780
|
);
|
|
@@ -2612,33 +2784,33 @@ function printExplore(action, result) {
|
|
|
2612
2784
|
}
|
|
2613
2785
|
for (const missing of result.missingPaths ?? []) {
|
|
2614
2786
|
const near = result.suggestions?.[missing];
|
|
2615
|
-
console.log(
|
|
2787
|
+
console.log(chalk10.yellow(` Not found: ${missing}${near?.length ? ` \u2014 did you mean ${near.join(", ")}?` : ""}`));
|
|
2616
2788
|
}
|
|
2617
2789
|
} else {
|
|
2618
2790
|
const commits = result.commit ? [result.commit] : result.commits ?? [];
|
|
2619
2791
|
for (const commit of commits) {
|
|
2620
|
-
console.log(` ${
|
|
2621
|
-
console.log(
|
|
2792
|
+
console.log(` ${chalk10.yellow(commit.revision.slice(0, 10))} ${commit.message.split("\n")[0]}`);
|
|
2793
|
+
console.log(chalk10.gray(` ${commit.author} \u2014 ${commit.date}`));
|
|
2622
2794
|
}
|
|
2623
2795
|
for (const changed of result.commit?.changedFiles ?? []) {
|
|
2624
|
-
console.log(
|
|
2796
|
+
console.log(chalk10.gray(` ${changed.change.padEnd(8)} ${changed.path}`));
|
|
2625
2797
|
}
|
|
2626
2798
|
}
|
|
2627
2799
|
if (result.truncated) {
|
|
2628
|
-
console.log(
|
|
2800
|
+
console.log(chalk10.yellow(" The list was capped \u2014 raise --limit to see more."));
|
|
2629
2801
|
}
|
|
2630
2802
|
if (result.revision) {
|
|
2631
2803
|
console.log();
|
|
2632
|
-
console.log(
|
|
2804
|
+
console.log(chalk10.gray(` revision: ${result.revision} (pass to \`anima edit --base-revision\`)`));
|
|
2633
2805
|
}
|
|
2634
2806
|
console.log();
|
|
2635
2807
|
}
|
|
2636
2808
|
|
|
2637
2809
|
// src/commands/edit.ts
|
|
2638
|
-
import { Command as
|
|
2810
|
+
import { Command as Command18 } from "commander";
|
|
2639
2811
|
import fs5 from "fs";
|
|
2640
|
-
import
|
|
2641
|
-
var editCommand = new
|
|
2812
|
+
import chalk11 from "chalk";
|
|
2813
|
+
var editCommand = new Command18("edit").summary("Change an artifact's files and commit them").description(
|
|
2642
2814
|
"Change an artifact's files and commit them, without git. All changes land as one commit."
|
|
2643
2815
|
).argument("<artifactUrl>", "Artifact URL or session ID").requiredOption("-m, --message <message>", "Commit message").option(
|
|
2644
2816
|
"--base-revision <rev>",
|
|
@@ -2660,7 +2832,7 @@ var editCommand = new Command16("edit").summary("Change an artifact's files and
|
|
|
2660
2832
|
printSuccess("Committed.");
|
|
2661
2833
|
for (const changed of result.changedFiles ?? []) {
|
|
2662
2834
|
const detail = changed.op === "move" ? `${changed.fromPath} \u2192 ${changed.path}` : changed.replacements !== void 0 ? `${changed.path} (${changed.replacements} replaced)` : changed.path;
|
|
2663
|
-
console.log(` ${
|
|
2835
|
+
console.log(` ${chalk11.gray(changed.op.padEnd(12))} ${detail}`);
|
|
2664
2836
|
}
|
|
2665
2837
|
console.log();
|
|
2666
2838
|
printKeyValue("Revision", result.revision ?? "unknown");
|
|
@@ -2758,11 +2930,11 @@ function readFile(path7) {
|
|
|
2758
2930
|
}
|
|
2759
2931
|
|
|
2760
2932
|
// src/commands/uploadAsset.ts
|
|
2761
|
-
import { Command as
|
|
2933
|
+
import { Command as Command19 } from "commander";
|
|
2762
2934
|
import fs6 from "fs";
|
|
2763
2935
|
import path5 from "path";
|
|
2764
|
-
import
|
|
2765
|
-
var uploadAssetCommand = new
|
|
2936
|
+
import chalk12 from "chalk";
|
|
2937
|
+
var uploadAssetCommand = new Command19("upload-asset").summary("Add an image, font or media file over 256 KB").description(
|
|
2766
2938
|
"Add a large image, font or media file (over 256 KB) to an artifact: stage it, upload it, and commit it at --path."
|
|
2767
2939
|
).argument("<artifactUrl>", "Artifact URL or session ID").argument("<file>", "Local file to upload").option(
|
|
2768
2940
|
"--path <path>",
|
|
@@ -2799,7 +2971,7 @@ var uploadAssetCommand = new Command17("upload-asset").summary("Add an image, fo
|
|
|
2799
2971
|
console.log();
|
|
2800
2972
|
console.log(" Commit it where it belongs with:");
|
|
2801
2973
|
console.log(
|
|
2802
|
-
` ${
|
|
2974
|
+
` ${chalk12.cyan(
|
|
2803
2975
|
`anima edit ${sessionId} -m "Add asset" --changes '[{"op":"write","path":"public/${path5.basename(file)}","assetUploadId":"${grant.assetUploadId}"}]'`
|
|
2804
2976
|
)}`
|
|
2805
2977
|
);
|
|
@@ -2876,8 +3048,8 @@ function readAsset(file) {
|
|
|
2876
3048
|
}
|
|
2877
3049
|
|
|
2878
3050
|
// src/commands/delete.ts
|
|
2879
|
-
import { Command as
|
|
2880
|
-
var deleteCommand = new
|
|
3051
|
+
import { Command as Command20 } from "commander";
|
|
3052
|
+
var deleteCommand = new Command20("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
3053
|
const mode = detectOutputMode(opts);
|
|
2882
3054
|
initLogger(opts.logFile);
|
|
2883
3055
|
const progress = new ProgressReporter(mode, opts.verbose);
|
|
@@ -2918,9 +3090,9 @@ var deleteCommand = new Command18("delete").summary("Delete an artifact (reversi
|
|
|
2918
3090
|
});
|
|
2919
3091
|
|
|
2920
3092
|
// src/commands/review.ts
|
|
2921
|
-
import { Command as
|
|
2922
|
-
import
|
|
2923
|
-
var listSubcommand = new
|
|
3093
|
+
import { Command as Command21 } from "commander";
|
|
3094
|
+
import chalk13 from "chalk";
|
|
3095
|
+
var listSubcommand = new Command21("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
3096
|
const mode = detectOutputMode(opts);
|
|
2925
3097
|
initLogger(opts.logFile);
|
|
2926
3098
|
await withCommandErrors(mode, async () => {
|
|
@@ -2935,7 +3107,7 @@ var listSubcommand = new Command19("list").summary("Read the comments addressed
|
|
|
2935
3107
|
}
|
|
2936
3108
|
});
|
|
2937
3109
|
});
|
|
2938
|
-
var resolveSubcommand = new
|
|
3110
|
+
var resolveSubcommand = new Command21("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
3111
|
const mode = detectOutputMode(opts);
|
|
2940
3112
|
initLogger(opts.logFile);
|
|
2941
3113
|
await withCommandErrors(mode, async () => {
|
|
@@ -2954,7 +3126,7 @@ var resolveSubcommand = new Command19("resolve").summary("Close one comment with
|
|
|
2954
3126
|
}
|
|
2955
3127
|
});
|
|
2956
3128
|
});
|
|
2957
|
-
var replySubcommand = new
|
|
3129
|
+
var replySubcommand = new Command21("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
3130
|
const mode = detectOutputMode(opts);
|
|
2959
3131
|
initLogger(opts.logFile);
|
|
2960
3132
|
await withCommandErrors(mode, async () => {
|
|
@@ -2973,7 +3145,7 @@ var replySubcommand = new Command19("reply").summary("Answer one comment, leavin
|
|
|
2973
3145
|
}
|
|
2974
3146
|
});
|
|
2975
3147
|
});
|
|
2976
|
-
var reviewCommand = new
|
|
3148
|
+
var reviewCommand = new Command21("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
3149
|
function parseLimit2(value) {
|
|
2978
3150
|
const limit = Number(value);
|
|
2979
3151
|
if (!Number.isInteger(limit) || limit < 1 || limit > 50) {
|
|
@@ -2990,29 +3162,29 @@ function printReviews(result) {
|
|
|
2990
3162
|
return;
|
|
2991
3163
|
}
|
|
2992
3164
|
for (const review of reviews) {
|
|
2993
|
-
console.log(` ${
|
|
2994
|
-
console.log(` ${
|
|
3165
|
+
console.log(` ${chalk13.bold(`Review ${review.reviewId}`)} ${chalk13.gray(`by ${review.author.name}`)}`);
|
|
3166
|
+
console.log(` ${chalk13.cyan.underline(review.artifactUrl)}`);
|
|
2995
3167
|
if (!review.isLatest) {
|
|
2996
3168
|
console.log(
|
|
2997
|
-
|
|
3169
|
+
chalk13.yellow(" The artifact moved since this was written \u2014 check the comments still apply.")
|
|
2998
3170
|
);
|
|
2999
3171
|
}
|
|
3000
3172
|
console.log();
|
|
3001
3173
|
for (const comment of review.comments) {
|
|
3002
3174
|
const marks = [
|
|
3003
|
-
comment.status !== "open" ?
|
|
3004
|
-
comment.unassigned ?
|
|
3175
|
+
comment.status !== "open" ? chalk13.gray(comment.status) : null,
|
|
3176
|
+
comment.unassigned ? chalk13.yellow("unassigned") : null
|
|
3005
3177
|
].filter(Boolean).join(" ");
|
|
3006
|
-
console.log(` ${
|
|
3007
|
-
console.log(
|
|
3178
|
+
console.log(` ${chalk13.bold(`#${comment.index}`)} ${comment.body}${marks ? ` ${marks}` : ""}`);
|
|
3179
|
+
console.log(chalk13.gray(` ${comment.commentId}${describeAnchor(comment)}`));
|
|
3008
3180
|
for (const reply of comment.replies ?? []) {
|
|
3009
|
-
console.log(
|
|
3181
|
+
console.log(chalk13.gray(` \u21B3 ${reply.author.name}: ${reply.body}`));
|
|
3010
3182
|
}
|
|
3011
3183
|
console.log();
|
|
3012
3184
|
}
|
|
3013
3185
|
printKeyValue("To close these in a commit", "");
|
|
3014
3186
|
for (const line of review.resolveTrailer.split("\n")) {
|
|
3015
|
-
console.log(` ${
|
|
3187
|
+
console.log(` ${chalk13.gray(line)}`);
|
|
3016
3188
|
}
|
|
3017
3189
|
console.log();
|
|
3018
3190
|
}
|
|
@@ -3033,13 +3205,13 @@ function describeAnchor(comment) {
|
|
|
3033
3205
|
}
|
|
3034
3206
|
|
|
3035
3207
|
// src/commands/skill.ts
|
|
3036
|
-
import { Command as
|
|
3208
|
+
import { Command as Command22 } from "commander";
|
|
3037
3209
|
import fs7 from "fs";
|
|
3038
3210
|
import path6 from "path";
|
|
3039
|
-
import
|
|
3211
|
+
import chalk14 from "chalk";
|
|
3040
3212
|
var GUIDE_PATH = "/guide.md";
|
|
3041
3213
|
function createSkillCommand() {
|
|
3042
|
-
return new
|
|
3214
|
+
return new Command22("skill").summary("Install the AgentGrid guide as a skill your agent can read").description(
|
|
3043
3215
|
"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
3216
|
).argument(
|
|
3045
3217
|
"[path]",
|
|
@@ -3066,8 +3238,8 @@ ${body}
|
|
|
3066
3238
|
logEvent("skill_installed", { path: target, bytes: document.length });
|
|
3067
3239
|
if (mode === "pretty") {
|
|
3068
3240
|
console.log();
|
|
3069
|
-
printSuccess(`Skill written to ${
|
|
3070
|
-
console.log(` ${
|
|
3241
|
+
printSuccess(`Skill written to ${chalk14.cyan(target)}`);
|
|
3242
|
+
console.log(` ${chalk14.gray(`${document.length} bytes, from ${apiUrl}${GUIDE_PATH}`)}`);
|
|
3071
3243
|
console.log();
|
|
3072
3244
|
} else {
|
|
3073
3245
|
outputResult(mode, {
|
|
@@ -3196,7 +3368,7 @@ function writeDocument(target, document) {
|
|
|
3196
3368
|
}
|
|
3197
3369
|
|
|
3198
3370
|
// src/index.ts
|
|
3199
|
-
var program = new
|
|
3371
|
+
var program = new Command23().name("anima").description(
|
|
3200
3372
|
`AgentGrid CLI \u2014 create, change, publish and share artifacts on AgentGrid.
|
|
3201
3373
|
An artifact is a real git repository, and every command below runs one
|
|
3202
3374
|
AgentGrid MCP tool for you. The command list is at the bottom; run
|
|
@@ -3206,6 +3378,7 @@ THE MAIN FLOW (build something and put it on AgentGrid):
|
|
|
3206
3378
|
1. anima login # once; a human approves
|
|
3207
3379
|
2. anima create -t import --from ./my-project # your code \u2192 a live app
|
|
3208
3380
|
(or: create -t empty --framework html \u2014 an empty repo you push to)
|
|
3381
|
+
(can create in several workspaces? add --workspace <id>; see "workspaces")
|
|
3209
3382
|
3. Change it. Two doors, same git repository:
|
|
3210
3383
|
- no clone: "explore" to read the files, "edit" to commit them. Needs no
|
|
3211
3384
|
shell, no git binary and no network of its own \u2014 prefer it.
|
|
@@ -3249,8 +3422,10 @@ OUTPUT: pretty text in a terminal; a single JSON object on stdout when piped
|
|
|
3249
3422
|
).version(version, "-v, --version");
|
|
3250
3423
|
program.addCommand(loginCommand);
|
|
3251
3424
|
program.addCommand(logoutCommand);
|
|
3425
|
+
program.addCommand(workspacesCommand);
|
|
3252
3426
|
program.addCommand(listCommand);
|
|
3253
3427
|
program.addCommand(createCommand);
|
|
3428
|
+
program.addCommand(createKnowledgeCommand);
|
|
3254
3429
|
program.addCommand(statusCommand);
|
|
3255
3430
|
program.addCommand(exploreCommand);
|
|
3256
3431
|
program.addCommand(editCommand);
|