@khangal.j/fireside-cli 0.0.5 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +293 -28
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -24,7 +24,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
));
|
|
25
25
|
|
|
26
26
|
// src/index.ts
|
|
27
|
-
var
|
|
27
|
+
var import_promises3 = require("fs/promises");
|
|
28
28
|
var import_commander = require("commander");
|
|
29
29
|
var import_picocolors = __toESM(require("picocolors"));
|
|
30
30
|
|
|
@@ -487,6 +487,51 @@ function openBrowser(url) {
|
|
|
487
487
|
}
|
|
488
488
|
}
|
|
489
489
|
|
|
490
|
+
// src/lib/project-config.ts
|
|
491
|
+
var import_promises2 = require("fs/promises");
|
|
492
|
+
var import_node_path2 = require("path");
|
|
493
|
+
var PROJECT_CONFIG_FILENAME = ".fireside.json";
|
|
494
|
+
async function loadProjectConfig(startDir = process.cwd()) {
|
|
495
|
+
const { root } = (0, import_node_path2.parse)(startDir);
|
|
496
|
+
let dir = startDir;
|
|
497
|
+
for (; ; ) {
|
|
498
|
+
const candidate = (0, import_node_path2.join)(dir, PROJECT_CONFIG_FILENAME);
|
|
499
|
+
try {
|
|
500
|
+
const raw = await (0, import_promises2.readFile)(candidate, "utf8");
|
|
501
|
+
return { config: JSON.parse(raw), path: candidate };
|
|
502
|
+
} catch (error) {
|
|
503
|
+
const code = error.code;
|
|
504
|
+
if (error instanceof SyntaxError) {
|
|
505
|
+
throw new Error(
|
|
506
|
+
`Invalid ${PROJECT_CONFIG_FILENAME} at ${candidate}: ${error.message}`
|
|
507
|
+
);
|
|
508
|
+
}
|
|
509
|
+
if (code !== "ENOENT") {
|
|
510
|
+
return null;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
if (dir === root) {
|
|
514
|
+
return null;
|
|
515
|
+
}
|
|
516
|
+
const parent = (0, import_node_path2.dirname)(dir);
|
|
517
|
+
if (parent === dir) {
|
|
518
|
+
return null;
|
|
519
|
+
}
|
|
520
|
+
dir = parent;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
async function loadDefaultProject(startDir) {
|
|
524
|
+
const loaded = await loadProjectConfig(startDir);
|
|
525
|
+
const project = loaded?.config.project;
|
|
526
|
+
return typeof project === "string" && project.trim() ? project.trim() : void 0;
|
|
527
|
+
}
|
|
528
|
+
async function writeProjectConfig(dir, config) {
|
|
529
|
+
const path = (0, import_node_path2.join)(dir, PROJECT_CONFIG_FILENAME);
|
|
530
|
+
await (0, import_promises2.writeFile)(path, `${JSON.stringify(config, null, 2)}
|
|
531
|
+
`);
|
|
532
|
+
return path;
|
|
533
|
+
}
|
|
534
|
+
|
|
490
535
|
// src/index.ts
|
|
491
536
|
function printInfo(message) {
|
|
492
537
|
console.log(`${import_picocolors.default.cyan("[i]")} ${message}`);
|
|
@@ -708,6 +753,71 @@ function serializeTaskHandoff(taskEntry, taskHandoff) {
|
|
|
708
753
|
}
|
|
709
754
|
};
|
|
710
755
|
}
|
|
756
|
+
function shortId(id) {
|
|
757
|
+
return id.slice(0, 8);
|
|
758
|
+
}
|
|
759
|
+
function leanAssignees(assignees) {
|
|
760
|
+
return assignees.map(
|
|
761
|
+
(member) => member.username ? `@${member.username}` : member.email
|
|
762
|
+
);
|
|
763
|
+
}
|
|
764
|
+
function serializeTaskRow(taskEntry) {
|
|
765
|
+
const assignees = leanAssignees(taskEntry.task.assignees);
|
|
766
|
+
return {
|
|
767
|
+
id: shortId(taskEntry.task.id),
|
|
768
|
+
title: taskEntry.task.title,
|
|
769
|
+
project: taskEntry.project.title,
|
|
770
|
+
board: taskEntry.board.title,
|
|
771
|
+
column: taskEntry.column.title,
|
|
772
|
+
role: taskEntry.column.role,
|
|
773
|
+
position: taskEntry.task.position,
|
|
774
|
+
...taskEntry.task.dueDate ? { dueDate: taskEntry.task.dueDate } : {},
|
|
775
|
+
...assignees.length ? { assignees } : {},
|
|
776
|
+
...taskEntry.task.description ? { hasDescription: true } : {}
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
function serializeAssignedRow(task) {
|
|
780
|
+
const assignees = leanAssignees(task.assignees);
|
|
781
|
+
return {
|
|
782
|
+
id: shortId(task.id),
|
|
783
|
+
title: task.title,
|
|
784
|
+
project: task.projectTitle,
|
|
785
|
+
board: task.boardTitle,
|
|
786
|
+
column: task.columnTitle,
|
|
787
|
+
...task.dueDate ? { dueDate: task.dueDate } : {},
|
|
788
|
+
...assignees.length ? { assignees } : {},
|
|
789
|
+
...task.description ? { hasDescription: true } : {}
|
|
790
|
+
};
|
|
791
|
+
}
|
|
792
|
+
function leanHandoff(handoff) {
|
|
793
|
+
const handle = (user) => user.username ? `@${user.username}` : user.email;
|
|
794
|
+
return {
|
|
795
|
+
id: handoff.id,
|
|
796
|
+
status: handoff.status,
|
|
797
|
+
summary: handoff.summary,
|
|
798
|
+
from: handle(handoff.fromUser),
|
|
799
|
+
to: handle(handoff.toUser),
|
|
800
|
+
next: handle(handoff.nextAssigneeUser),
|
|
801
|
+
contextMarkdown: handoff.contextMarkdown,
|
|
802
|
+
createdAt: handoff.createdAt
|
|
803
|
+
};
|
|
804
|
+
}
|
|
805
|
+
function serializeTaskFull(taskEntry, contextRevision, taskHandoff) {
|
|
806
|
+
return {
|
|
807
|
+
id: taskEntry.task.id,
|
|
808
|
+
title: taskEntry.task.title,
|
|
809
|
+
project: taskEntry.project.title,
|
|
810
|
+
board: taskEntry.board.title,
|
|
811
|
+
column: taskEntry.column.title,
|
|
812
|
+
role: taskEntry.column.role,
|
|
813
|
+
position: taskEntry.task.position,
|
|
814
|
+
dueDate: taskEntry.task.dueDate,
|
|
815
|
+
assignees: leanAssignees(taskEntry.task.assignees),
|
|
816
|
+
description: taskEntry.task.description,
|
|
817
|
+
...contextRevision !== void 0 ? { contextRevision } : {},
|
|
818
|
+
...taskHandoff !== void 0 ? { handoff: taskHandoff ? leanHandoff(taskHandoff) : null } : {}
|
|
819
|
+
};
|
|
820
|
+
}
|
|
711
821
|
function printTaskSummary(taskEntry) {
|
|
712
822
|
console.log(formatHeading(taskEntry.task.title, taskEntry.task.id));
|
|
713
823
|
console.log(
|
|
@@ -778,7 +888,7 @@ function printTaskHandoff(taskEntry, taskHandoff) {
|
|
|
778
888
|
}
|
|
779
889
|
async function readTextFile(filePath, label) {
|
|
780
890
|
try {
|
|
781
|
-
return await (0,
|
|
891
|
+
return await (0, import_promises3.readFile)(filePath, "utf8");
|
|
782
892
|
} catch (error) {
|
|
783
893
|
const message = error instanceof Error ? error.message : `Unable to read ${label} file.`;
|
|
784
894
|
throw new Error(`Failed to read ${label} file: ${message}`);
|
|
@@ -862,6 +972,23 @@ async function loadCliContextWithCurrentUser(baseUrl) {
|
|
|
862
972
|
user: await getCurrentUser(cliContext.baseUrl, cliContext.accessToken)
|
|
863
973
|
};
|
|
864
974
|
}
|
|
975
|
+
async function resolveProjectSelector(explicit, allProjects) {
|
|
976
|
+
if (allProjects) {
|
|
977
|
+
return void 0;
|
|
978
|
+
}
|
|
979
|
+
if (explicit) {
|
|
980
|
+
return explicit;
|
|
981
|
+
}
|
|
982
|
+
return loadDefaultProject();
|
|
983
|
+
}
|
|
984
|
+
function findTaskMatches(entries, taskId) {
|
|
985
|
+
const trimmed = taskId.trim();
|
|
986
|
+
const exact = entries.filter((entry) => entry.task.id === trimmed);
|
|
987
|
+
if (exact.length || trimmed.length < 4) {
|
|
988
|
+
return exact;
|
|
989
|
+
}
|
|
990
|
+
return entries.filter((entry) => entry.task.id.startsWith(trimmed));
|
|
991
|
+
}
|
|
865
992
|
async function loadProjectBoardsResults(baseUrl, accessToken, projectSelector) {
|
|
866
993
|
const projects = await listProjects(baseUrl, accessToken);
|
|
867
994
|
const selectedProjects = projectSelector ? [resolveByIdOrTitle(projects, projectSelector, "project")] : projects;
|
|
@@ -873,13 +1000,30 @@ async function loadProjectBoardsResults(baseUrl, accessToken, projectSelector) {
|
|
|
873
1000
|
);
|
|
874
1001
|
}
|
|
875
1002
|
async function resolveTaskEntry(baseUrl, accessToken, taskId, projectSelector) {
|
|
876
|
-
const
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
1003
|
+
const scoped = await resolveProjectSelector(projectSelector, false);
|
|
1004
|
+
let matches = findTaskMatches(
|
|
1005
|
+
flattenTaskEntries(
|
|
1006
|
+
await loadProjectBoardsResults(baseUrl, accessToken, scoped)
|
|
1007
|
+
),
|
|
1008
|
+
taskId
|
|
1009
|
+
);
|
|
1010
|
+
if (!matches.length && scoped && !projectSelector) {
|
|
1011
|
+
matches = findTaskMatches(
|
|
1012
|
+
flattenTaskEntries(
|
|
1013
|
+
await loadProjectBoardsResults(baseUrl, accessToken, void 0)
|
|
1014
|
+
),
|
|
1015
|
+
taskId
|
|
1016
|
+
);
|
|
1017
|
+
}
|
|
1018
|
+
if (!matches.length) {
|
|
880
1019
|
throw new Error(`Task ${taskId} was not found.`);
|
|
881
1020
|
}
|
|
882
|
-
|
|
1021
|
+
if (matches.length > 1) {
|
|
1022
|
+
throw new Error(
|
|
1023
|
+
`Task id "${taskId}" is ambiguous (${matches.length} matches). Use more characters or the full id.`
|
|
1024
|
+
);
|
|
1025
|
+
}
|
|
1026
|
+
return matches[0];
|
|
883
1027
|
}
|
|
884
1028
|
function parseTargetPosition(value) {
|
|
885
1029
|
if (value === void 0) {
|
|
@@ -1015,7 +1159,7 @@ function printAssignedTasks(tasks, projectFilter) {
|
|
|
1015
1159
|
}
|
|
1016
1160
|
}
|
|
1017
1161
|
var program = new import_commander.Command();
|
|
1018
|
-
program.name("fireside").description("Fireside CLI").version("0.0.
|
|
1162
|
+
program.name("fireside").description("Fireside CLI").version("0.0.7").configureOutput({
|
|
1019
1163
|
outputError: (message, write) => write(import_picocolors.default.red(message))
|
|
1020
1164
|
}).showHelpAfterError();
|
|
1021
1165
|
addBaseUrlOption(
|
|
@@ -1084,18 +1228,23 @@ addBaseUrlOption(
|
|
|
1084
1228
|
})
|
|
1085
1229
|
);
|
|
1086
1230
|
addBaseUrlOption(
|
|
1087
|
-
program.command("my-stuff").description("List tasks currently assigned to you").option("--json", "Print assigned tasks as JSON").option("-p, --project <project>", "Filter by project id or title").action(
|
|
1231
|
+
program.command("my-stuff").description("List tasks currently assigned to you").option("--json", "Print assigned tasks as compact JSON").option("--raw", "With --json, print the full unabridged shape (larger)").option("-p, --project <project>", "Filter by project id or title").option("--all-projects", "Ignore the default project; span all projects").action(
|
|
1088
1232
|
async (options) => {
|
|
1089
1233
|
const state = await requireAuthState();
|
|
1090
1234
|
const configState = await loadConfigState();
|
|
1091
1235
|
const baseUrl = await resolveBaseUrl(options.baseUrl, configState);
|
|
1092
|
-
const
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
options.project
|
|
1236
|
+
const projectFilter = await resolveProjectSelector(
|
|
1237
|
+
options.project,
|
|
1238
|
+
options.allProjects
|
|
1096
1239
|
);
|
|
1240
|
+
const tasks = await listAssignedTasks(baseUrl, state.accessToken);
|
|
1241
|
+
const filteredTasks = filterAssignedTasksByProject(tasks, projectFilter);
|
|
1097
1242
|
if (options.json) {
|
|
1098
|
-
|
|
1243
|
+
if (options.raw) {
|
|
1244
|
+
console.log(JSON.stringify(filteredTasks, null, 2));
|
|
1245
|
+
} else {
|
|
1246
|
+
console.log(JSON.stringify(filteredTasks.map(serializeAssignedRow)));
|
|
1247
|
+
}
|
|
1099
1248
|
return;
|
|
1100
1249
|
}
|
|
1101
1250
|
printAssignedTasks(filteredTasks, options.project);
|
|
@@ -1114,13 +1263,50 @@ addBaseUrlOption(
|
|
|
1114
1263
|
printProjects(projects);
|
|
1115
1264
|
})
|
|
1116
1265
|
);
|
|
1266
|
+
var configCommand = program.command("config").description("Manage CLI configuration");
|
|
1267
|
+
addBaseUrlOption(
|
|
1268
|
+
configCommand.command("show").description("Show resolved defaults (base URL, default project)").action(async (options) => {
|
|
1269
|
+
const configState = await loadConfigState();
|
|
1270
|
+
const baseUrl = await resolveBaseUrl(options.baseUrl, configState);
|
|
1271
|
+
const loaded = await loadProjectConfig();
|
|
1272
|
+
printKeyValue("Base URL", baseUrl);
|
|
1273
|
+
if (loaded?.config.project) {
|
|
1274
|
+
printKeyValue("Default project", loaded.config.project);
|
|
1275
|
+
printKeyValue(" from", loaded.path);
|
|
1276
|
+
} else {
|
|
1277
|
+
printKeyValue("Default project", import_picocolors.default.dim("Not set"));
|
|
1278
|
+
}
|
|
1279
|
+
})
|
|
1280
|
+
);
|
|
1281
|
+
addBaseUrlOption(
|
|
1282
|
+
configCommand.command("use-project <project>").description(
|
|
1283
|
+
`Set the default project for this directory (writes ${PROJECT_CONFIG_FILENAME})`
|
|
1284
|
+
).action(async (project, options) => {
|
|
1285
|
+
const { accessToken, baseUrl } = await loadCliContext(options.baseUrl);
|
|
1286
|
+
const projects = await listProjects(baseUrl, accessToken);
|
|
1287
|
+
const resolved = resolveByIdOrTitle(projects, project, "project");
|
|
1288
|
+
const path = await writeProjectConfig(process.cwd(), {
|
|
1289
|
+
project: resolved.title
|
|
1290
|
+
});
|
|
1291
|
+
printSuccess(`Default project set to ${import_picocolors.default.cyan(resolved.title)}.`);
|
|
1292
|
+
printKeyValue("Wrote", path);
|
|
1293
|
+
})
|
|
1294
|
+
);
|
|
1117
1295
|
var tasksCommand = program.command("tasks").description("Interact with tasks");
|
|
1118
1296
|
addBaseUrlOption(
|
|
1119
|
-
tasksCommand.command("list").description(
|
|
1297
|
+
tasksCommand.command("list").description(
|
|
1298
|
+
"List tasks assigned to you in the default project (.fireside.json)"
|
|
1299
|
+
).option("--json", "Print tasks as compact JSON").option("--raw", "With --json, print the full unabridged shape (larger)").option("-p, --project <project>", "Filter by project id or title").option("--board <board>", "Filter by board id or title").option("--column <column>", "Filter by column id or title").option("--all", "Show everyone's tasks across all projects").option("--everyone", "Include tasks not assigned to you").option("--all-projects", "Ignore the default project; span all projects").action(
|
|
1120
1300
|
async (options) => {
|
|
1121
1301
|
const { accessToken, baseUrl } = await loadCliContext(options.baseUrl);
|
|
1302
|
+
const includeEveryone = Boolean(options.all || options.everyone);
|
|
1303
|
+
const spanAllProjects = Boolean(options.all || options.allProjects);
|
|
1304
|
+
const projectSelector = await resolveProjectSelector(
|
|
1305
|
+
options.project,
|
|
1306
|
+
spanAllProjects
|
|
1307
|
+
);
|
|
1122
1308
|
let taskEntries = flattenTaskEntries(
|
|
1123
|
-
await loadProjectBoardsResults(baseUrl, accessToken,
|
|
1309
|
+
await loadProjectBoardsResults(baseUrl, accessToken, projectSelector)
|
|
1124
1310
|
);
|
|
1125
1311
|
if (options.board) {
|
|
1126
1312
|
taskEntries = taskEntries.filter(
|
|
@@ -1132,14 +1318,30 @@ addBaseUrlOption(
|
|
|
1132
1318
|
(taskEntry) => matchIdOrTitle(taskEntry.column, options.column)
|
|
1133
1319
|
);
|
|
1134
1320
|
}
|
|
1135
|
-
if (
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
2
|
|
1321
|
+
if (!includeEveryone) {
|
|
1322
|
+
const currentUser = await getCurrentUser(baseUrl, accessToken);
|
|
1323
|
+
taskEntries = taskEntries.filter(
|
|
1324
|
+
(taskEntry) => taskEntry.task.assignees.some(
|
|
1325
|
+
(assignee) => assignee.id === currentUser.id
|
|
1141
1326
|
)
|
|
1142
1327
|
);
|
|
1328
|
+
}
|
|
1329
|
+
if (options.json) {
|
|
1330
|
+
if (options.raw) {
|
|
1331
|
+
console.log(
|
|
1332
|
+
JSON.stringify(
|
|
1333
|
+
taskEntries.map((taskEntry) => serializeTaskEntry(taskEntry)),
|
|
1334
|
+
null,
|
|
1335
|
+
2
|
|
1336
|
+
)
|
|
1337
|
+
);
|
|
1338
|
+
} else {
|
|
1339
|
+
console.log(
|
|
1340
|
+
JSON.stringify(
|
|
1341
|
+
taskEntries.map((taskEntry) => serializeTaskRow(taskEntry))
|
|
1342
|
+
)
|
|
1343
|
+
);
|
|
1344
|
+
}
|
|
1143
1345
|
return;
|
|
1144
1346
|
}
|
|
1145
1347
|
if (!taskEntries.length) {
|
|
@@ -1153,7 +1355,52 @@ addBaseUrlOption(
|
|
|
1153
1355
|
)
|
|
1154
1356
|
);
|
|
1155
1357
|
addBaseUrlOption(
|
|
1156
|
-
tasksCommand.command("
|
|
1358
|
+
tasksCommand.command("columns").description("List a board's columns (id, role, title) without tasks").option("--json", "Print columns as JSON").option("-p, --project <project>", "Filter by project id or title").option("--board <board>", "Board id or title").action(
|
|
1359
|
+
async (options) => {
|
|
1360
|
+
const { accessToken, baseUrl } = await loadCliContext(options.baseUrl);
|
|
1361
|
+
const projectSelector = await resolveProjectSelector(
|
|
1362
|
+
options.project,
|
|
1363
|
+
options.allProjects
|
|
1364
|
+
);
|
|
1365
|
+
const [projectBoardsResult] = await loadProjectBoardsResults(
|
|
1366
|
+
baseUrl,
|
|
1367
|
+
accessToken,
|
|
1368
|
+
projectSelector
|
|
1369
|
+
);
|
|
1370
|
+
if (!projectBoardsResult) {
|
|
1371
|
+
printWarning("No project found.");
|
|
1372
|
+
return;
|
|
1373
|
+
}
|
|
1374
|
+
const board = resolveBoard(
|
|
1375
|
+
projectBoardsResult.boardsData.boards,
|
|
1376
|
+
options.board
|
|
1377
|
+
);
|
|
1378
|
+
const columns = [...board.columns].sort(
|
|
1379
|
+
(left, right) => left.position - right.position
|
|
1380
|
+
);
|
|
1381
|
+
if (options.json) {
|
|
1382
|
+
console.log(
|
|
1383
|
+
JSON.stringify(
|
|
1384
|
+
columns.map((column) => ({
|
|
1385
|
+
id: column.id,
|
|
1386
|
+
role: column.role,
|
|
1387
|
+
title: column.title
|
|
1388
|
+
}))
|
|
1389
|
+
)
|
|
1390
|
+
);
|
|
1391
|
+
return;
|
|
1392
|
+
}
|
|
1393
|
+
console.log(
|
|
1394
|
+
formatHeading(`${projectBoardsResult.project.title} / ${board.title}`)
|
|
1395
|
+
);
|
|
1396
|
+
for (const column of columns) {
|
|
1397
|
+
console.log(` ${import_picocolors.default.dim(column.role.padEnd(8))} ${column.title}`);
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
)
|
|
1401
|
+
);
|
|
1402
|
+
addBaseUrlOption(
|
|
1403
|
+
tasksCommand.command("get <taskId>").description("Show a task by id (accepts a short id prefix)").option("--json", "Print the task as JSON").option("--raw", "With --json, print the full unabridged shape (larger)").option("-p, --project <project>", "Limit lookup to a project id or title").option("--context", "Load the latest saved task context").option("--handoff", "Load the active handoff context").action(
|
|
1157
1404
|
async (taskId, options) => {
|
|
1158
1405
|
const { accessToken, baseUrl } = await loadCliContext(options.baseUrl);
|
|
1159
1406
|
const taskEntry = await resolveTaskEntry(
|
|
@@ -1179,7 +1426,7 @@ addBaseUrlOption(
|
|
|
1179
1426
|
if (options.json) {
|
|
1180
1427
|
console.log(
|
|
1181
1428
|
JSON.stringify(
|
|
1182
|
-
serializeTaskEntry(taskEntry, contextRevision, taskHandoff),
|
|
1429
|
+
options.raw ? serializeTaskEntry(taskEntry, contextRevision, taskHandoff) : serializeTaskFull(taskEntry, contextRevision, taskHandoff),
|
|
1183
1430
|
null,
|
|
1184
1431
|
2
|
|
1185
1432
|
)
|
|
@@ -1191,7 +1438,10 @@ addBaseUrlOption(
|
|
|
1191
1438
|
)
|
|
1192
1439
|
);
|
|
1193
1440
|
addBaseUrlOption(
|
|
1194
|
-
tasksCommand.command("create").description("Create a task").
|
|
1441
|
+
tasksCommand.command("create").description("Create a task").option(
|
|
1442
|
+
"-p, --project <project>",
|
|
1443
|
+
"Project id or title (defaults to .fireside.json)"
|
|
1444
|
+
).requiredOption("-t, --title <title>", "Task title").requiredOption("-c, --column <column>", "Column id or title").option("--board <board>", "Board id or title").option("--description <description>", "Task description").option("--description-file <path>", "Read task description from a file").option("--due-date <date>", "Due date in YYYY-MM-DD format").option(
|
|
1195
1445
|
"-a, --assignee <member>",
|
|
1196
1446
|
"Assign a member by @username, email, id, or me",
|
|
1197
1447
|
collectOptionValue,
|
|
@@ -1199,10 +1449,16 @@ addBaseUrlOption(
|
|
|
1199
1449
|
).option("--json", "Print the created task as JSON").action(
|
|
1200
1450
|
async (options) => {
|
|
1201
1451
|
const { accessToken, baseUrl, user } = await loadCliContextWithCurrentUser(options.baseUrl);
|
|
1452
|
+
const projectSelector = options.project ?? await loadDefaultProject();
|
|
1453
|
+
if (!projectSelector) {
|
|
1454
|
+
throw new Error(
|
|
1455
|
+
`No project. Pass -p <project> or add ${PROJECT_CONFIG_FILENAME} (\`fireside config use-project <project>\`).`
|
|
1456
|
+
);
|
|
1457
|
+
}
|
|
1202
1458
|
const [projectBoardsResult] = await loadProjectBoardsResults(
|
|
1203
1459
|
baseUrl,
|
|
1204
1460
|
accessToken,
|
|
1205
|
-
|
|
1461
|
+
projectSelector
|
|
1206
1462
|
);
|
|
1207
1463
|
const board = resolveBoard(
|
|
1208
1464
|
projectBoardsResult.boardsData.boards,
|
|
@@ -1249,7 +1505,10 @@ addBaseUrlOption(
|
|
|
1249
1505
|
)
|
|
1250
1506
|
);
|
|
1251
1507
|
addBaseUrlOption(
|
|
1252
|
-
tasksCommand.command("bulk-create").description("Create many tasks at once from a file").
|
|
1508
|
+
tasksCommand.command("bulk-create").description("Create many tasks at once from a file").option(
|
|
1509
|
+
"-p, --project <project>",
|
|
1510
|
+
"Project id or title (defaults to .fireside.json)"
|
|
1511
|
+
).requiredOption(
|
|
1253
1512
|
"--file <path>",
|
|
1254
1513
|
"Tasks file: JSON array, `{ tasks: [...] }`, or one title per line"
|
|
1255
1514
|
).option("--board <board>", "Board id or title").option(
|
|
@@ -1269,6 +1528,12 @@ addBaseUrlOption(
|
|
|
1269
1528
|
).option("--dry-run", "Resolve and preview tasks without creating them").option("--json", "Print results as JSON").action(
|
|
1270
1529
|
async (options) => {
|
|
1271
1530
|
const { accessToken, baseUrl, user } = await loadCliContextWithCurrentUser(options.baseUrl);
|
|
1531
|
+
const projectSelector = options.project ?? await loadDefaultProject();
|
|
1532
|
+
if (!projectSelector) {
|
|
1533
|
+
throw new Error(
|
|
1534
|
+
`No project. Pass -p <project> or add ${PROJECT_CONFIG_FILENAME} (\`fireside config use-project <project>\`).`
|
|
1535
|
+
);
|
|
1536
|
+
}
|
|
1272
1537
|
const rows = parseBulkTaskRows(
|
|
1273
1538
|
await readTextFile(options.file, "tasks")
|
|
1274
1539
|
);
|
|
@@ -1278,7 +1543,7 @@ addBaseUrlOption(
|
|
|
1278
1543
|
const [projectBoardsResult] = await loadProjectBoardsResults(
|
|
1279
1544
|
baseUrl,
|
|
1280
1545
|
accessToken,
|
|
1281
|
-
|
|
1546
|
+
projectSelector
|
|
1282
1547
|
);
|
|
1283
1548
|
const board = resolveBoard(
|
|
1284
1549
|
projectBoardsResult.boardsData.boards,
|