@forloop-cc/forloop-cli 0.2.3 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/forloop +119 -178
  2. package/package.json +1 -1
package/dist/forloop CHANGED
@@ -912,7 +912,6 @@ function createAuthStatusCommand() {
912
912
  } else {
913
913
  console.log("Not authenticated");
914
914
  console.log("Run: forloop auth login --api-key <token>");
915
- process.exit(1);
916
915
  }
917
916
  }
918
917
  });
@@ -929,6 +928,25 @@ function createAuthLogoutCommand() {
929
928
  });
930
929
  }
931
930
 
931
+ // src/output/formatter.ts
932
+ function print(config, data, label) {
933
+ if (config.output === "json") {
934
+ console.log(JSON.stringify(data, null, 2));
935
+ } else if (typeof data === "string") {
936
+ if (label && !config.quiet) console.log(label);
937
+ console.log(data);
938
+ } else {
939
+ if (label && !config.quiet) console.log(label);
940
+ console.log(JSON.stringify(data, null, 2));
941
+ }
942
+ }
943
+ function formatError(message, format) {
944
+ if (format === "json") {
945
+ return JSON.stringify({ error: { message } }, null, 2);
946
+ }
947
+ return `Error: ${message}`;
948
+ }
949
+
932
950
  // src/commands/story.ts
933
951
  function createStoryCommands() {
934
952
  return {
@@ -945,7 +963,7 @@ async function getClient() {
945
963
  console.error("Error: No API token configured. Run: forloop auth login --api-key <token>");
946
964
  process.exit(1);
947
965
  }
948
- const client = new ForLoopAPIClient({ token, baseUrl: config.apiUrl });
966
+ const client = new ForLoopAPIClient({ token, baseUrl: config.apiUrl, timeout: 3e4 });
949
967
  return { client };
950
968
  }
951
969
  function createStoryCreateCommand() {
@@ -967,7 +985,7 @@ function createStoryCreateCommand() {
967
985
  'forloop story create --title "Meeting notes" --sprint 1 --type basic-note',
968
986
  'forloop story create --title "Design Docs" --sprint 1'
969
987
  ],
970
- async run(_config, flags) {
988
+ async run(config, flags) {
971
989
  const resolved = await getClient();
972
990
  if (!resolved) return;
973
991
  const { client } = resolved;
@@ -1007,7 +1025,7 @@ function createStoryCreateCommand() {
1007
1025
  points: flags.points
1008
1026
  })
1009
1027
  });
1010
- console.log(`Story created: #${story.id} - ${story.title} (${story.type})`);
1028
+ print(config, story, `Story created: #${story.id} - ${story.title} (${story.type})`);
1011
1029
  } else {
1012
1030
  const story = await client.createStory({
1013
1031
  title: flags.title,
@@ -1017,7 +1035,7 @@ function createStoryCreateCommand() {
1017
1035
  priority: flags.priority || "medium",
1018
1036
  status: "todo"
1019
1037
  });
1020
- console.log(`Doc folder created: #${story.id} - ${story.title}`);
1038
+ print(config, story, `Doc folder created: #${story.id} - ${story.title}`);
1021
1039
  }
1022
1040
  }
1023
1041
  });
@@ -1032,7 +1050,7 @@ function createStoryGetCommand() {
1032
1050
  { flag: "--no-comments", description: "Exclude comments" }
1033
1051
  ],
1034
1052
  examples: ["forloop story get --id 123"],
1035
- async run(_config, flags) {
1053
+ async run(config, flags) {
1036
1054
  const resolved = await getClient();
1037
1055
  if (!resolved) return;
1038
1056
  const { client } = resolved;
@@ -1042,28 +1060,7 @@ function createStoryGetCommand() {
1042
1060
  process.exit(1);
1043
1061
  }
1044
1062
  const story = await client.getStory(storyId, { includeComments: !flags.noComments });
1045
- const lines = [
1046
- `Story #${story.id}: ${story.title}`,
1047
- `Sprint: #${story.sprint?.id || "N/A"} ${story.sprint?.title || ""}`,
1048
- `Status: ${story.status}`,
1049
- `Priority: ${story.priority}`,
1050
- `Type: ${story.type}`
1051
- ];
1052
- if (story.points) lines.push(`Points: ${story.points}`);
1053
- if (story.assignee) lines.push(`Assignee: ${story.assignee.name || story.assignee.email || "Unknown"}`);
1054
- if (story.assigneeAgentKey) lines.push(`Agent: ${story.assigneeAgentKey}`);
1055
- if (story.description) {
1056
- lines.push("", "Description:", story.description);
1057
- }
1058
- if (!flags.noComments && story.comments?.length) {
1059
- lines.push("", `--- Comments (${story.comments.length}) ---`);
1060
- for (const comment of story.comments) {
1061
- const author = comment.authorAgentKey || comment.user?.name || "Unknown";
1062
- lines.push(` ${author}: ${comment.content || comment.body || ""}`);
1063
- if (comment.artifacts?.length) lines.push(` Artifacts: ${comment.artifacts.join(", ")}`);
1064
- }
1065
- }
1066
- console.log(lines.join("\n"));
1063
+ print(config, story);
1067
1064
  }
1068
1065
  });
1069
1066
  }
@@ -1081,7 +1078,7 @@ function createStoryUpdateCommand() {
1081
1078
  { flag: "--points <n>", description: "Story points (0-10)", type: "number" }
1082
1079
  ],
1083
1080
  examples: ["forloop story update --id 123 --status in_progress"],
1084
- async run(_config, flags) {
1081
+ async run(config, flags) {
1085
1082
  const resolved = await getClient();
1086
1083
  if (!resolved) return;
1087
1084
  const { client } = resolved;
@@ -1101,7 +1098,7 @@ function createStoryUpdateCommand() {
1101
1098
  process.exit(1);
1102
1099
  }
1103
1100
  const story = await client.updateStory(storyId, data);
1104
- console.log(`Story #${story.id} updated successfully.`);
1101
+ print(config, story, `Story #${story.id} updated successfully.`);
1105
1102
  }
1106
1103
  });
1107
1104
  }
@@ -1115,7 +1112,7 @@ function createStoryDeleteCommand() {
1115
1112
  { flag: "--confirm", description: "Confirm deletion" }
1116
1113
  ],
1117
1114
  examples: ["forloop story delete --id 123 --confirm"],
1118
- async run(_config, flags) {
1115
+ async run(config, flags) {
1119
1116
  if (!flags.confirm) {
1120
1117
  console.error("Error: --confirm is required to permanently delete a story.");
1121
1118
  process.exit(1);
@@ -1124,7 +1121,7 @@ function createStoryDeleteCommand() {
1124
1121
  if (!resolved) return;
1125
1122
  const { client } = resolved;
1126
1123
  await client.deleteStory(flags.id);
1127
- console.log(`Story #${flags.id} deleted successfully.`);
1124
+ print(config, { deleted: flags.id }, `Story #${flags.id} deleted successfully.`);
1128
1125
  }
1129
1126
  });
1130
1127
  }
@@ -1146,12 +1143,9 @@ async function getClient2() {
1146
1143
  console.error("Error: No API token configured. Run: forloop auth login --api-key <token>");
1147
1144
  process.exit(1);
1148
1145
  }
1149
- const client = new ForLoopAPIClient({ token, baseUrl: config.apiUrl });
1146
+ const client = new ForLoopAPIClient({ token, baseUrl: config.apiUrl, timeout: 3e4 });
1150
1147
  return { client };
1151
1148
  }
1152
- function formatDate(dateString) {
1153
- return new Date(dateString).toLocaleDateString("en-US", { year: "numeric", month: "short", day: "numeric" });
1154
- }
1155
1149
  function createSprintGetCommand() {
1156
1150
  return defineCommand({
1157
1151
  name: "sprint get",
@@ -1163,7 +1157,7 @@ function createSprintGetCommand() {
1163
1157
  { flag: "--no-files", description: "Exclude files from response" }
1164
1158
  ],
1165
1159
  examples: ["forloop sprint get --id 456", "forloop sprint get --id 456 --no-files"],
1166
- async run(_config, flags) {
1160
+ async run(config, flags) {
1167
1161
  const resolved = await getClient2();
1168
1162
  if (!resolved) return;
1169
1163
  const { client } = resolved;
@@ -1176,20 +1170,7 @@ function createSprintGetCommand() {
1176
1170
  includeStories: !flags.noStories,
1177
1171
  includeFiles: !flags.noFiles
1178
1172
  });
1179
- const lines = [
1180
- `Sprint #${sprint.id}: ${sprint.title}`,
1181
- `Status: ${sprint.status}`,
1182
- `Period: ${formatDate(sprint.startDate)} - ${formatDate(sprint.endDate)}`,
1183
- `Stories: ${sprint.stories?.length || 0}`
1184
- ];
1185
- if (sprint.stories?.length) {
1186
- lines.push("", "Stories:", "");
1187
- for (const story of sprint.stories) {
1188
- const icons = { todo: "[ ]", in_progress: "[>]", done: "[x]", blocked: "[!]" };
1189
- lines.push(` ${icons[story.status] || "[?]"} #${story.id} ${story.title} (${story.status})`);
1190
- }
1191
- }
1192
- console.log(lines.join("\n"));
1173
+ print(config, sprint);
1193
1174
  }
1194
1175
  });
1195
1176
  }
@@ -1203,23 +1184,15 @@ function createSprintListCommand() {
1203
1184
  { flag: "--include-system-org", description: "Include system organization sprints" }
1204
1185
  ],
1205
1186
  examples: ["forloop sprint list", "forloop sprint list --org-id 1"],
1206
- async run(_config, flags) {
1187
+ async run(config, flags) {
1207
1188
  const resolved = await getClient2();
1208
1189
  if (!resolved) return;
1209
1190
  const { client } = resolved;
1210
1191
  const params = {};
1211
1192
  if (flags.orgId) params.organizationId = String(flags.orgId);
1193
+ if (flags.includeSystemOrg) params.includeSystemOrg = "true";
1212
1194
  const sprints = await client.listSprints(params);
1213
- if (!sprints.length) {
1214
- console.log("No sprints found.");
1215
- return;
1216
- }
1217
- const lines = ["Sprints:", ""];
1218
- for (const sprint of sprints) {
1219
- const orgInfo = sprint.organizationName ? ` (${sprint.organizationName})` : "";
1220
- lines.push(` #${sprint.id} ${sprint.title}${orgInfo} - ${sprint.status}`);
1221
- }
1222
- console.log(lines.join("\n"));
1195
+ print(config, sprints);
1223
1196
  }
1224
1197
  });
1225
1198
  }
@@ -1237,7 +1210,7 @@ function createSprintCreateCommand() {
1237
1210
  { flag: "--org-id <id>", description: "Organization ID", type: "number" }
1238
1211
  ],
1239
1212
  examples: ['forloop sprint create --title "Sprint 15" --start-date 2026-06-09 --end-date 2026-06-23'],
1240
- async run(_config, flags) {
1213
+ async run(config, flags) {
1241
1214
  const resolved = await getClient2();
1242
1215
  if (!resolved) return;
1243
1216
  const { client } = resolved;
@@ -1253,8 +1226,7 @@ function createSprintCreateCommand() {
1253
1226
  isPrivate: flags.private === true,
1254
1227
  organizationId: flags.orgId
1255
1228
  });
1256
- console.log(`Sprint created: #${sprint.id} - ${sprint.title}`);
1257
- console.log(`Period: ${formatDate(sprint.startDate)} - ${formatDate(sprint.endDate)}`);
1229
+ print(config, sprint, `Sprint created: #${sprint.id} - ${sprint.title}`);
1258
1230
  }
1259
1231
  });
1260
1232
  }
@@ -1272,7 +1244,7 @@ function createSprintUpdateCommand() {
1272
1244
  { flag: "--private", description: "Set visibility to private" }
1273
1245
  ],
1274
1246
  examples: ['forloop sprint update --id 456 --title "Updated Sprint"'],
1275
- async run(_config, flags) {
1247
+ async run(config, flags) {
1276
1248
  const resolved = await getClient2();
1277
1249
  if (!resolved) return;
1278
1250
  const { client } = resolved;
@@ -1287,7 +1259,7 @@ function createSprintUpdateCommand() {
1287
1259
  process.exit(1);
1288
1260
  }
1289
1261
  const sprint = await client.updateSprint(flags.id, updateData);
1290
- console.log(`Sprint #${sprint.id} updated successfully.`);
1262
+ print(config, sprint, `Sprint #${sprint.id} updated successfully.`);
1291
1263
  }
1292
1264
  });
1293
1265
  }
@@ -1301,7 +1273,7 @@ function createSprintDeleteCommand() {
1301
1273
  { flag: "--confirm", description: "Confirm deletion" }
1302
1274
  ],
1303
1275
  examples: ["forloop sprint delete --id 456 --confirm"],
1304
- async run(_config, flags) {
1276
+ async run(config, flags) {
1305
1277
  if (!flags.confirm) {
1306
1278
  console.error("Error: --confirm is required to permanently delete a sprint.");
1307
1279
  process.exit(1);
@@ -1309,21 +1281,13 @@ function createSprintDeleteCommand() {
1309
1281
  const resolved = await getClient2();
1310
1282
  if (!resolved) return;
1311
1283
  const { client } = resolved;
1312
- await client.deleteSprint(flags.id);
1313
- console.log(`Sprint #${flags.id} deleted successfully.`);
1284
+ const result = await client.deleteSprint(flags.id);
1285
+ print(config, result, `Sprint #${flags.id} deleted successfully.`);
1314
1286
  }
1315
1287
  });
1316
1288
  }
1317
1289
 
1318
1290
  // src/commands/org.ts
1319
- function createOrgCommands() {
1320
- return {
1321
- "org list": createOrgListCommand(),
1322
- "org get": createOrgGetCommand(),
1323
- "org create": createOrgCreateCommand(),
1324
- "org update": createOrgUpdateCommand()
1325
- };
1326
- }
1327
1291
  async function getClient3() {
1328
1292
  const config = getConfig();
1329
1293
  const token = await getToken();
@@ -1331,7 +1295,15 @@ async function getClient3() {
1331
1295
  console.error("Error: No API token configured. Run: forloop auth login --api-key <token>");
1332
1296
  process.exit(1);
1333
1297
  }
1334
- return { client: new ForLoopAPIClient({ token, baseUrl: config.apiUrl }) };
1298
+ return { client: new ForLoopAPIClient({ token, baseUrl: config.apiUrl, timeout: 3e4 }) };
1299
+ }
1300
+ function createOrgCommands() {
1301
+ return {
1302
+ "org list": createOrgListCommand(),
1303
+ "org get": createOrgGetCommand(),
1304
+ "org create": createOrgCreateCommand(),
1305
+ "org update": createOrgUpdateCommand()
1306
+ };
1335
1307
  }
1336
1308
  function createOrgListCommand() {
1337
1309
  return defineCommand({
@@ -1341,12 +1313,16 @@ function createOrgListCommand() {
1341
1313
  options: [
1342
1314
  { flag: "--owned-only", description: "List only organizations you own" }
1343
1315
  ],
1344
- examples: ["forloop org list", "forloop org list --owned-only"],
1345
- async run(_config, flags) {
1316
+ examples: ["forloop org list", "forloop org list --owned-only", "forloop org list --output json"],
1317
+ async run(config, flags) {
1346
1318
  const resolved = await getClient3();
1347
1319
  if (!resolved) return;
1348
1320
  const { client } = resolved;
1349
1321
  const orgs = flags.ownedOnly ? await client.getOwnedOrganizations() : await client.listOrganizations();
1322
+ if (config.output === "json") {
1323
+ print(config, orgs);
1324
+ return;
1325
+ }
1350
1326
  if (!orgs.length) {
1351
1327
  console.log("No organizations found.");
1352
1328
  return;
@@ -1368,19 +1344,16 @@ function createOrgGetCommand() {
1368
1344
  options: [
1369
1345
  { flag: "--id <number>", description: "Organization ID", required: true, type: "number" }
1370
1346
  ],
1371
- examples: ["forloop org get --id 1"],
1372
- async run(_config, flags) {
1347
+ examples: ["forloop org get --id 1", "forloop org get --id 1 --output json"],
1348
+ async run(config, flags) {
1373
1349
  if (!flags.id) {
1374
1350
  console.error("Error: --id is required");
1375
1351
  process.exit(1);
1376
1352
  }
1377
1353
  const resolved = await getClient3();
1378
1354
  if (!resolved) return;
1379
- const { client } = resolved;
1380
- const org = await client.getOrganization(flags.id);
1381
- console.log(`Organization #${org.id}: ${org.name}`);
1382
- console.log(`Type: ${org.type || "user"}`);
1383
- if (org.description) console.log(`Description: ${org.description}`);
1355
+ const org = await resolved.client.getOrganization(flags.id);
1356
+ print(config, org);
1384
1357
  }
1385
1358
  });
1386
1359
  }
@@ -1394,19 +1367,18 @@ function createOrgCreateCommand() {
1394
1367
  { flag: "--description <text>", description: "Organization description" }
1395
1368
  ],
1396
1369
  examples: ['forloop org create --name "My Team" --description "Engineering team"'],
1397
- async run(_config, flags) {
1370
+ async run(config, flags) {
1398
1371
  if (!flags.name) {
1399
1372
  console.error("Error: --name is required");
1400
1373
  process.exit(1);
1401
1374
  }
1402
1375
  const resolved = await getClient3();
1403
1376
  if (!resolved) return;
1404
- const { client } = resolved;
1405
- const org = await client.createOrganization({
1377
+ const org = await resolved.client.createOrganization({
1406
1378
  name: flags.name,
1407
1379
  description: flags.description
1408
1380
  });
1409
- console.log(`Organization created: #${org.id} - ${org.name}`);
1381
+ print(config, org, `Organization created: #${org.id} - ${org.name}`);
1410
1382
  }
1411
1383
  });
1412
1384
  }
@@ -1421,19 +1393,18 @@ function createOrgUpdateCommand() {
1421
1393
  { flag: "--description <text>", description: "New description" }
1422
1394
  ],
1423
1395
  examples: ['forloop org update --id 1 --name "New Name"'],
1424
- async run(_config, flags) {
1396
+ async run(config, flags) {
1425
1397
  if (!flags.id) {
1426
1398
  console.error("Error: --id is required");
1427
1399
  process.exit(1);
1428
1400
  }
1429
1401
  const resolved = await getClient3();
1430
1402
  if (!resolved) return;
1431
- const { client } = resolved;
1432
- const org = await client.updateOrganization(flags.id, {
1403
+ const org = await resolved.client.updateOrganization(flags.id, {
1433
1404
  name: flags.name,
1434
1405
  description: flags.description
1435
1406
  });
1436
- console.log(`Organization #${org.id} updated successfully.`);
1407
+ print(config, org, `Organization #${org.id} updated successfully.`);
1437
1408
  }
1438
1409
  });
1439
1410
  }
@@ -1447,30 +1418,41 @@ function createTemplateCommands() {
1447
1418
  function createTemplateListCommand() {
1448
1419
  return defineCommand({
1449
1420
  name: "template list",
1450
- description: "List available story templates",
1421
+ description: "List available story templates (basic-task, basic-note, schedule-meeting, doc-folder)",
1451
1422
  usage: "forloop template list",
1452
- examples: ["forloop template list"],
1453
- async run(_config) {
1454
- const config = getConfig();
1423
+ examples: ["forloop template list", "forloop template list --output json"],
1424
+ async run(config) {
1425
+ const apiConfig = getConfig();
1455
1426
  const token = await getToken();
1456
1427
  if (!token) {
1457
1428
  console.error("Error: No API token configured.");
1458
1429
  process.exit(1);
1459
1430
  }
1460
- const client = new ForLoopAPIClient({ token, baseUrl: config.apiUrl });
1431
+ const client = new ForLoopAPIClient({ token, baseUrl: apiConfig.apiUrl, timeout: 3e4 });
1461
1432
  const templates = await client.listTemplates();
1462
1433
  const active = templates.filter((t) => !t.deletedAt);
1434
+ if (config.output === "json") {
1435
+ print(config, active);
1436
+ return;
1437
+ }
1463
1438
  if (!active.length) {
1464
1439
  console.log("No templates found.");
1465
1440
  return;
1466
1441
  }
1442
+ const builtIn = [
1443
+ { name: "Basic Task", slug: "basic-task", description: "Standard task with title, description, priority, and story points", fields: [] },
1444
+ { name: "Basic Note", slug: "basic-note", description: "Simple note with title and description", fields: [] }
1445
+ ];
1446
+ const allTemplates = [...builtIn, ...active];
1467
1447
  const lines = ["Available Templates:", ""];
1468
- for (const t of active) {
1469
- const fields = JSON.parse(t.fields);
1470
- const fieldNames = fields.map((f) => f.label).join(", ");
1471
- lines.push(` ${t.name} (slug: ${t.slug})`);
1448
+ for (const t of allTemplates) {
1449
+ const fields = t.fields && typeof t.fields === "string" ? JSON.parse(t.fields) : Array.isArray(t.fields) ? t.fields : [];
1450
+ const fieldNames = Array.isArray(fields) ? fields.map((f) => f.label || f).join(", ") : "";
1451
+ const isBuiltIn = builtIn.some((b) => b.slug === t.slug);
1452
+ const badge = isBuiltIn ? " (built-in)" : "";
1453
+ lines.push(` ${t.name} (slug: ${t.slug})${badge}`);
1472
1454
  lines.push(` ${t.description}`);
1473
- lines.push(` Fields: ${fieldNames}`);
1455
+ if (fieldNames) lines.push(` Fields: ${fieldNames}`);
1474
1456
  lines.push("");
1475
1457
  }
1476
1458
  console.log(lines.join("\n"));
@@ -1493,7 +1475,7 @@ async function getClient4() {
1493
1475
  console.error("Error: No API token configured.");
1494
1476
  process.exit(1);
1495
1477
  }
1496
- return { client: new ForLoopAPIClient({ token, baseUrl: config.apiUrl }) };
1478
+ return { client: new ForLoopAPIClient({ token, baseUrl: config.apiUrl, timeout: 3e4 }) };
1497
1479
  }
1498
1480
  function createAgentHistoryCommand() {
1499
1481
  return defineCommand({
@@ -1505,7 +1487,7 @@ function createAgentHistoryCommand() {
1505
1487
  { flag: "--limit <n>", description: "Number of messages (max 200, default 50)", type: "number" }
1506
1488
  ],
1507
1489
  examples: ["forloop agent history --sprint 1 --limit 20"],
1508
- async run(_config, flags) {
1490
+ async run(config, flags) {
1509
1491
  const resolved = await getClient4();
1510
1492
  if (!resolved) return;
1511
1493
  const { client } = resolved;
@@ -1515,21 +1497,7 @@ function createAgentHistoryCommand() {
1515
1497
  sprintId,
1516
1498
  limit: flags.limit || 50
1517
1499
  });
1518
- const messages = history.messages || [];
1519
- if (!messages.length) {
1520
- console.log("No conversation history found.");
1521
- return;
1522
- }
1523
- console.log(`Conversation History (${history.total || messages.length} total):`);
1524
- for (const msg of messages) {
1525
- const preview = (msg.userMessage || "").substring(0, 80);
1526
- console.log(` ${preview}${preview.length >= 80 ? "..." : ""}`);
1527
- if (msg.agentResponse) {
1528
- const resp = msg.agentResponse.substring(0, 100);
1529
- console.log(` -> ${resp}${msg.agentResponse.length > 100 ? "..." : ""}`);
1530
- }
1531
- console.log("");
1532
- }
1500
+ print(config, history);
1533
1501
  }
1534
1502
  });
1535
1503
  }
@@ -1605,13 +1573,6 @@ function createDeveloperStatusCommand() {
1605
1573
  }
1606
1574
 
1607
1575
  // src/commands/user.ts
1608
- function createUserCommands() {
1609
- return {
1610
- "user profile": createUserProfileCommand(),
1611
- "user quotas": createUserQuotasCommand(),
1612
- "org quotas": createOrgQuotasCommand()
1613
- };
1614
- }
1615
1576
  async function getClient5() {
1616
1577
  const config = getConfig();
1617
1578
  const token = await getToken();
@@ -1619,22 +1580,26 @@ async function getClient5() {
1619
1580
  console.error("Error: No API token configured.");
1620
1581
  process.exit(1);
1621
1582
  }
1622
- return { client: new ForLoopAPIClient({ token, baseUrl: config.apiUrl }) };
1583
+ return { client: new ForLoopAPIClient({ token, baseUrl: config.apiUrl, timeout: 3e4 }) };
1584
+ }
1585
+ function createUserCommands() {
1586
+ return {
1587
+ "user profile": createUserProfileCommand(),
1588
+ "user quotas": createUserQuotasCommand(),
1589
+ "org quotas": createOrgQuotasCommand()
1590
+ };
1623
1591
  }
1624
1592
  function createUserProfileCommand() {
1625
1593
  return defineCommand({
1626
1594
  name: "user profile",
1627
1595
  description: "Get current user profile",
1628
1596
  usage: "forloop user profile",
1629
- examples: ["forloop user profile"],
1630
- async run(_config) {
1597
+ examples: ["forloop user profile", "forloop user profile --output json"],
1598
+ async run(config) {
1631
1599
  const resolved = await getClient5();
1632
1600
  if (!resolved) return;
1633
- const { client } = resolved;
1634
- const profile = await client.getUserProfile();
1635
- console.log(`User: ${profile.name || profile.email || "Unknown"}`);
1636
- console.log(`Email: ${profile.email}`);
1637
- if (profile.tier) console.log(`Tier: ${profile.tier}`);
1601
+ const profile = await resolved.client.getUserProfile();
1602
+ print(config, profile, "User Profile:");
1638
1603
  }
1639
1604
  });
1640
1605
  }
@@ -1643,14 +1608,12 @@ function createUserQuotasCommand() {
1643
1608
  name: "user quotas",
1644
1609
  description: "Check user quota limits",
1645
1610
  usage: "forloop user quotas",
1646
- examples: ["forloop user quotas"],
1647
- async run(_config) {
1611
+ examples: ["forloop user quotas", "forloop user quotas --output json"],
1612
+ async run(config) {
1648
1613
  const resolved = await getClient5();
1649
1614
  if (!resolved) return;
1650
- const { client } = resolved;
1651
- const quotas = await client.getUserQuotas();
1652
- console.log("User Quotas:");
1653
- console.log(JSON.stringify(quotas, null, 2));
1615
+ const quotas = await resolved.client.getUserQuotas();
1616
+ print(config, quotas, "User Quotas:");
1654
1617
  }
1655
1618
  });
1656
1619
  }
@@ -1662,18 +1625,16 @@ function createOrgQuotasCommand() {
1662
1625
  options: [
1663
1626
  { flag: "--org-id <id>", description: "Organization ID", required: true, type: "number" }
1664
1627
  ],
1665
- examples: ["forloop org quotas --org-id 1"],
1666
- async run(_config, flags) {
1628
+ examples: ["forloop org quotas --org-id 1", "forloop org quotas --org-id 1 --output json"],
1629
+ async run(config, flags) {
1667
1630
  if (!flags.orgId) {
1668
1631
  console.error("Error: --org-id is required");
1669
1632
  process.exit(1);
1670
1633
  }
1671
1634
  const resolved = await getClient5();
1672
1635
  if (!resolved) return;
1673
- const { client } = resolved;
1674
- const quotas = await client.getOrganizationQuotas(flags.orgId);
1675
- console.log("Organization Quotas:");
1676
- console.log(JSON.stringify(quotas, null, 2));
1636
+ const quotas = await resolved.client.getOrganizationQuotas(flags.orgId);
1637
+ print(config, quotas, "Organization Quotas:");
1677
1638
  }
1678
1639
  });
1679
1640
  }
@@ -1696,7 +1657,7 @@ async function getClient6() {
1696
1657
  console.error("Error: No API token configured.");
1697
1658
  process.exit(1);
1698
1659
  }
1699
- return { client: new ForLoopAPIClient({ token, baseUrl: config.apiUrl }) };
1660
+ return { client: new ForLoopAPIClient({ token, baseUrl: config.apiUrl, timeout: 3e4 }) };
1700
1661
  }
1701
1662
  var mimeTypes = {
1702
1663
  ".png": "image/png",
@@ -1730,7 +1691,7 @@ function createFileUploadCommand() {
1730
1691
  { flag: "--folder <text>", description: "S3 folder within sprint" }
1731
1692
  ],
1732
1693
  examples: ["forloop file upload --path ./report.pdf --sprint 1 --folder docs"],
1733
- async run(_config, flags) {
1694
+ async run(config, flags) {
1734
1695
  const resolved = await getClient6();
1735
1696
  if (!resolved) return;
1736
1697
  const { client } = resolved;
@@ -1766,8 +1727,7 @@ function createFileUploadCommand() {
1766
1727
  size: fileContent.length,
1767
1728
  folder: flags.folder
1768
1729
  });
1769
- console.log(`File uploaded: ${fileName} (${formatSize(fileContent.length)})`);
1770
- console.log(`URL: ${complete.url}`);
1730
+ print(config, { fileName, size: fileContent.length, url: complete.url, fileId: complete.id }, `File uploaded: ${fileName} (${formatSize(fileContent.length)})`);
1771
1731
  }
1772
1732
  });
1773
1733
  }
@@ -1780,23 +1740,12 @@ function createFileListCommand() {
1780
1740
  { flag: "--sprint <id>", description: "Sprint ID", required: true, type: "number" }
1781
1741
  ],
1782
1742
  examples: ["forloop file list --sprint 1"],
1783
- async run(_config, flags) {
1743
+ async run(config, flags) {
1784
1744
  const resolved = await getClient6();
1785
1745
  if (!resolved) return;
1786
1746
  const { client } = resolved;
1787
1747
  const files = await client.getSprintFiles(flags.sprint);
1788
- if (!files.length) {
1789
- console.log("No files found.");
1790
- return;
1791
- }
1792
- const lines = [`Files in Sprint #${flags.sprint}:`, ""];
1793
- for (const file of files) {
1794
- lines.push(` ${file.originalName || file.filename}`);
1795
- lines.push(` Size: ${formatSize(file.size)} | Type: ${file.fileType}`);
1796
- lines.push(` URL: ${file.url}`);
1797
- lines.push("");
1798
- }
1799
- console.log(lines.join("\n"));
1748
+ print(config, files, `Files in Sprint #${flags.sprint}:`);
1800
1749
  }
1801
1750
  });
1802
1751
  }
@@ -1868,7 +1817,7 @@ function createFolderCreateCommand() {
1868
1817
  console.error("Error: No API token configured.");
1869
1818
  process.exit(1);
1870
1819
  }
1871
- const client = new ForLoopAPIClient({ token, baseUrl: config.apiUrl });
1820
+ const client = new ForLoopAPIClient({ token, baseUrl: config.apiUrl, timeout: 3e4 });
1872
1821
  const story = await client.createStory({
1873
1822
  title: flags.title,
1874
1823
  description: flags.description,
@@ -1900,7 +1849,7 @@ async function getClient7() {
1900
1849
  console.error("Error: No API token configured.");
1901
1850
  process.exit(1);
1902
1851
  }
1903
- return { client: new ForLoopAPIClient({ token, baseUrl: config.apiUrl }) };
1852
+ return { client: new ForLoopAPIClient({ token, baseUrl: config.apiUrl, timeout: 3e4 }) };
1904
1853
  }
1905
1854
  function createSyncAivyFolderCommand() {
1906
1855
  return defineCommand({
@@ -2332,14 +2281,6 @@ var registry = new CommandRegistry({
2332
2281
  "agent sprint": agentDeveloperSprintCmd
2333
2282
  });
2334
2283
 
2335
- // src/output/formatter.ts
2336
- function formatError(message, format) {
2337
- if (format === "json") {
2338
- return JSON.stringify({ error: { message } }, null, 2);
2339
- }
2340
- return `\u274C ${message}`;
2341
- }
2342
-
2343
2284
  // src/errors/handler.ts
2344
2285
  function handleError(error) {
2345
2286
  const isJson = process.argv.includes("--output=json");
@@ -2362,7 +2303,7 @@ Tip: ${suggestion}
2362
2303
  }
2363
2304
 
2364
2305
  // src/main.ts
2365
- var CLI_VERSION = "0.2.3";
2306
+ var CLI_VERSION = "0.2.4";
2366
2307
  process.on("SIGINT", () => {
2367
2308
  process.stderr.write("\nInterrupted. Exiting.\n");
2368
2309
  process.exit(130);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forloop-cc/forloop-cli",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "description": "Command-line interface for the ForLoop AI development platform",
5
5
  "type": "module",
6
6
  "license": "MIT",