@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.
- package/dist/forloop +119 -178
- 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(
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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
|
|
1380
|
-
|
|
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(
|
|
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
|
|
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
|
-
|
|
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(
|
|
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
|
|
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
|
-
|
|
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(
|
|
1454
|
-
const
|
|
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:
|
|
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
|
|
1469
|
-
const fields = JSON.parse(t.fields);
|
|
1470
|
-
const fieldNames = fields.map((f) => f.label).join(", ");
|
|
1471
|
-
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
|
1634
|
-
|
|
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(
|
|
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
|
|
1651
|
-
|
|
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(
|
|
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
|
|
1674
|
-
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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.
|
|
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);
|