@shortcut-cli/shortcut-cli 3.8.1 → 5.0.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.
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env node
2
+ import { loadConfig } from "../lib/configure.js";
3
+ import client from "../lib/client.js";
4
+ import spinner from "../lib/spinner.js";
5
+ import { Command } from "commander";
6
+ import chalk from "chalk";
7
+ //#region src/bin/short-iterations.ts
8
+ const config = loadConfig();
9
+ const log = console.log;
10
+ const spin = spinner();
11
+ const opts = new Command().description("Display iterations available for stories").option("-S, --status [status]", "Filter by status (unstarted, started, done)", "").option("-T, --team [id|name]", "Filter by team/group id or name", "").option("-C, --current", "Show only current/active iterations", false).option("-t, --title [query]", "Filter iterations with name containing query", "").option("-d, --detailed", "Show more details for each iteration", false).option("-f, --format [template]", "Format each iteration output by template", "").parse(process.argv).opts();
12
+ const main = async () => {
13
+ spin.start();
14
+ const [iterations, groups] = await Promise.all([client.listIterations().then((r) => r.data), client.listGroups().then((r) => r.data)]);
15
+ spin.stop(true);
16
+ const textMatch = new RegExp(opts.title ?? "", "i");
17
+ const statusMatch = opts.status ? new RegExp(opts.status, "i") : null;
18
+ const groupsById = new Map(groups.map((g) => [g.id, g]));
19
+ const now = /* @__PURE__ */ new Date();
20
+ iterations.filter((iteration) => {
21
+ if (!iteration.name.match(textMatch)) return false;
22
+ if (statusMatch && !iteration.status.match(statusMatch)) return false;
23
+ if (opts.team) {
24
+ const teamMatch = new RegExp(opts.team, "i");
25
+ if (!iteration.group_ids?.map((id) => groupsById.get(id)).find((g) => g && (g.id.match(teamMatch) || g.name.match(teamMatch)))) return false;
26
+ }
27
+ if (opts.current) {
28
+ const start = new Date(iteration.start_date);
29
+ const end = new Date(iteration.end_date);
30
+ if (now < start || now > end) return false;
31
+ }
32
+ return true;
33
+ }).sort((a, b) => {
34
+ return new Date(b.start_date).getTime() - new Date(a.start_date).getTime();
35
+ }).forEach((iteration) => printItem(iteration, groupsById));
36
+ };
37
+ const printItem = (iteration, groupsById) => {
38
+ const stats = iteration.stats;
39
+ const groups = iteration.group_ids?.map((id) => groupsById.get(id)?.name).filter(Boolean) ?? [];
40
+ const totalStories = stats.num_stories_done + stats.num_stories_started + stats.num_stories_unstarted + stats.num_stories_backlog;
41
+ let defaultFormat = `#%id %t\nStatus:\t\t%s\nStart:\t\t%start\nEnd:\t\t%end\n`;
42
+ defaultFormat += `Teams:\t\t%teams\n`;
43
+ defaultFormat += `Stories:\t%stories (%done done)\nPoints:\t\t%points (%pdone done)\n`;
44
+ if (opts.detailed) {
45
+ defaultFormat += `Completion:\t%completion%\n`;
46
+ defaultFormat += `URL:\t\t%url\n`;
47
+ }
48
+ const format = opts.format || defaultFormat;
49
+ const url = `https://app.shortcut.com/${config.urlSlug}/iteration/${iteration.id}`;
50
+ const completionPct = stats.num_points > 0 ? Math.round(stats.num_points_done / stats.num_points * 100) : 0;
51
+ log(format.replace(/%id/, chalk.bold(`${iteration.id}`)).replace(/%t/, chalk.blue(`${iteration.name}`)).replace(/%s/, formatStatus(iteration.status)).replace(/%start/, `${iteration.start_date}`).replace(/%end/, `${iteration.end_date}`).replace(/%teams/, groups.join(", ") || "_").replace(/%stories/, `${totalStories}`).replace(/%done/, `${stats.num_stories_done}`).replace(/%points/, `${stats.num_points}`).replace(/%pdone/, `${stats.num_points_done}`).replace(/%completion/, `${completionPct}`).replace(/%url/, url));
52
+ };
53
+ const formatStatus = (status) => {
54
+ switch (status) {
55
+ case "started": return chalk.green(status);
56
+ case "done": return chalk.gray(status);
57
+ default: return chalk.yellow(status);
58
+ }
59
+ };
60
+ main();
61
+ //#endregion
62
+ export {};
@@ -0,0 +1,130 @@
1
+ #!/usr/bin/env node
2
+ import client from "../lib/client.js";
3
+ import spinner from "../lib/spinner.js";
4
+ import stories_default from "../lib/stories.js";
5
+ import { Command } from "commander";
6
+ import chalk from "chalk";
7
+ //#region src/bin/short-label.ts
8
+ const spin = spinner();
9
+ const log = console.log;
10
+ const program = new Command().usage("[command] [options]").description("create labels or view stories for a label");
11
+ program.command("create").description("create a new label").option("-n, --name [text]", "Set name of label, required", "").option("-d, --description [text]", "Set description of label", "").option("-c, --color [hex]", "Set label color in hex format like #3366cc", "").option("-I, --idonly", "Print only ID of label result").action(createLabel);
12
+ program.command("update <idOrName>").description("update an existing label").option("-n, --name [text]", "Set name of label", "").option("-d, --description [text]", "Set description of label", "").option("-c, --color [hex]", "Set label color in hex format like #3366cc", "").option("-a, --archived", "Archive label").action(updateLabel);
13
+ program.command("stories <idOrName>").description("list stories for a label by id or name").option("-d, --detailed", "Show more details for each story").option("-f, --format [template]", "Format each story output by template", "").action(listLabelStories);
14
+ program.command("epics <idOrName>").description("list epics for a label by id or name").action(listLabelEpics);
15
+ program.parse(process.argv);
16
+ async function createLabel(options) {
17
+ if (!options.name) {
18
+ log("Must provide --name");
19
+ process.exit(1);
20
+ }
21
+ if (!options.idonly) spin.start();
22
+ try {
23
+ const input = { name: options.name };
24
+ if (options.description) input.description = options.description;
25
+ if (options.color) input.color = options.color;
26
+ const label = await client.createLabel(input).then((r) => r.data);
27
+ if (!options.idonly) spin.stop(true);
28
+ if (options.idonly) {
29
+ log(label.id);
30
+ return;
31
+ }
32
+ printLabel(label);
33
+ } catch (e) {
34
+ if (!options.idonly) spin.stop(true);
35
+ log(`Error creating label: ${e.message ?? String(e)}`);
36
+ process.exit(1);
37
+ }
38
+ }
39
+ async function updateLabel(idOrName, options) {
40
+ spin.start();
41
+ try {
42
+ const entities = await stories_default.fetchEntities();
43
+ const label = stories_default.findLabel(entities, idOrName);
44
+ if (!label) {
45
+ spin.stop(true);
46
+ log(`Label ${idOrName} not found`);
47
+ process.exit(1);
48
+ }
49
+ const input = {};
50
+ if (options.name) input.name = options.name;
51
+ if (options.description) input.description = options.description;
52
+ if (options.color) input.color = options.color;
53
+ if (options.archived) input.archived = true;
54
+ if (Object.keys(input).length === 0) {
55
+ spin.stop(true);
56
+ log("No updates provided. Use --name, --description, --color, or --archived");
57
+ process.exit(1);
58
+ }
59
+ const updatedLabel = await client.updateLabel(label.id, input).then((r) => r.data);
60
+ spin.stop(true);
61
+ printLabel(updatedLabel);
62
+ } catch (e) {
63
+ spin.stop(true);
64
+ log(`Error updating label: ${e.message ?? String(e)}`);
65
+ process.exit(1);
66
+ }
67
+ }
68
+ async function listLabelStories(idOrName, options) {
69
+ spin.start();
70
+ try {
71
+ const entities = await stories_default.fetchEntities();
72
+ const label = stories_default.findLabel(entities, idOrName);
73
+ if (!label) {
74
+ spin.stop(true);
75
+ log(`Label ${idOrName} not found`);
76
+ process.exit(1);
77
+ }
78
+ const stories = await client.listLabelStories(label.id).then((r) => r.data);
79
+ spin.stop(true);
80
+ if (stories.length === 0) {
81
+ log(`No stories found for label #${label.id} ${label.name}`);
82
+ return;
83
+ }
84
+ stories.map((story) => stories_default.hydrateStory(entities, story)).forEach(options.detailed ? (story) => stories_default.printDetailedStory(story, entities) : stories_default.printFormattedStory({ format: options.format }));
85
+ } catch (e) {
86
+ spin.stop(true);
87
+ log(`Error fetching label stories: ${e.message ?? String(e)}`);
88
+ process.exit(1);
89
+ }
90
+ }
91
+ async function listLabelEpics(idOrName) {
92
+ spin.start();
93
+ try {
94
+ const entities = await stories_default.fetchEntities();
95
+ const label = stories_default.findLabel(entities, idOrName);
96
+ if (!label) {
97
+ spin.stop(true);
98
+ log(`Label ${idOrName} not found`);
99
+ process.exit(1);
100
+ }
101
+ const epics = await client.listLabelEpics(label.id).then((r) => r.data);
102
+ spin.stop(true);
103
+ if (epics.length === 0) {
104
+ log(`No epics found for label #${label.id} ${label.name}`);
105
+ return;
106
+ }
107
+ epics.forEach(printEpic);
108
+ } catch (e) {
109
+ spin.stop(true);
110
+ log(`Error fetching label epics: ${e.message ?? String(e)}`);
111
+ process.exit(1);
112
+ }
113
+ }
114
+ function printLabel(label) {
115
+ log(chalk.bold(`#${label.id}`) + chalk.blue(` ${label.name}`));
116
+ if (label.color) log(chalk.bold("Color: ") + ` ${label.color}`);
117
+ if (label.description) log(chalk.bold("Description: ") + ` ${label.description}`);
118
+ if (label.archived) log(chalk.bold("Archived: ") + ` ${label.archived}`);
119
+ log();
120
+ }
121
+ function printEpic(epic) {
122
+ log(chalk.bold(`#${epic.id}`) + chalk.blue(` ${epic.name}`));
123
+ log(chalk.bold("State: ") + ` ${epic.state}`);
124
+ log(chalk.bold("Started: ") + ` ${epic.started}`);
125
+ log(chalk.bold("Completed: ") + ` ${epic.completed}`);
126
+ log(chalk.bold("URL: ") + ` ${epic.app_url}`);
127
+ log();
128
+ }
129
+ //#endregion
130
+ export {};
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env node
2
+ import client from "../lib/client.js";
3
+ import spinner from "../lib/spinner.js";
4
+ import { Command } from "commander";
5
+ import chalk from "chalk";
6
+ //#region src/bin/short-labels.ts
7
+ const spin = spinner();
8
+ const log = console.log;
9
+ const opts = new Command().description("Display labels available for stories and epics").option("-a, --archived", "List labels including archived", "").option("-s, --search [query]", "List labels with name containing query", "").parse(process.argv).opts();
10
+ async function main() {
11
+ spin.start();
12
+ const labels = await client.listLabels().then((r) => r.data);
13
+ spin.stop(true);
14
+ const searchMatch = new RegExp(opts.search ?? "", "i");
15
+ labels.filter((label) => !!`${label.id} ${label.name}`.match(searchMatch)).forEach(printLabel);
16
+ }
17
+ function printLabel(label) {
18
+ if (label.archived && !opts.archived) return;
19
+ log(chalk.bold(`#${label.id}`) + chalk.blue(` ${label.name}`));
20
+ if (label.color) log(chalk.bold("Color: ") + ` ${label.color}`);
21
+ if (label.description) log(chalk.bold("Description: ") + ` ${label.description}`);
22
+ if (label.archived) log(chalk.bold("Archived: ") + ` ${label.archived}`);
23
+ log();
24
+ }
25
+ main();
26
+ //#endregion
27
+ export {};
@@ -1,35 +1,31 @@
1
1
  #!/usr/bin/env node
2
- const require_rolldown_runtime = require('../_virtual/rolldown_runtime.js');
3
- const require_lib_spinner = require('../lib/spinner.js');
4
- const require_lib_client = require('../lib/client.js');
5
- let commander = require("commander");
6
- commander = require_rolldown_runtime.__toESM(commander);
7
- let chalk = require("chalk");
8
- chalk = require_rolldown_runtime.__toESM(chalk);
9
-
2
+ import client from "../lib/client.js";
3
+ import spinner from "../lib/spinner.js";
4
+ import { Command } from "commander";
5
+ import chalk from "chalk";
10
6
  //#region src/bin/short-members.ts
11
- const spin = require_lib_spinner.default("Loading... %s ");
7
+ const spin = spinner("Loading... %s ");
12
8
  const log = console.log;
13
- const program = commander.default.description("Display members available for stories").option("-s, --search [query]", "List members with name containing query", "").option("-d, --disabled", "List members including disabled", "").parse(process.argv);
9
+ const opts = new Command().description("Display members available for stories").option("-s, --search [query]", "List members with name containing query", "").option("-d, --disabled", "List members including disabled", "").parse(process.argv).opts();
14
10
  const main = async () => {
15
11
  spin.start();
16
- const members = await require_lib_client.default.listMembers(null).then((r) => r.data);
12
+ const members = await client.listMembers().then((r) => r.data);
17
13
  spin.stop(true);
18
- const ownerMatch = new RegExp(program.search, "i");
14
+ const ownerMatch = new RegExp(opts.search ?? "", "i");
19
15
  members.filter((o) => {
20
16
  return !!`${o.profile.name} ${o.profile.mention_name}`.match(ownerMatch);
21
17
  }).map(printMember);
22
18
  };
23
19
  const printMember = (member) => {
24
- if (member.disabled && !program.disabled) return;
25
- log(chalk.default.bold(`#${member.id}`));
26
- log(chalk.default.bold("Name: ") + ` ${member.profile.name}`);
27
- log(chalk.default.bold("Mention Name: ") + ` ${member.profile.mention_name}`);
28
- log(chalk.default.bold("Role: ") + ` ${member.role}`);
29
- log(chalk.default.bold("Email: ") + ` ${member.profile.email_address}`);
30
- if (member.disabled) log(chalk.default.bold("Disabled: ") + ` ${member.disabled}`);
20
+ if (member.disabled && !opts.disabled) return;
21
+ log(chalk.bold(`#${member.id}`));
22
+ log(chalk.bold("Name: ") + ` ${member.profile.name}`);
23
+ log(chalk.bold("Mention Name: ") + ` ${member.profile.mention_name}`);
24
+ log(chalk.bold("Role: ") + ` ${member.role}`);
25
+ log(chalk.bold("Email: ") + ` ${member.profile.email_address}`);
26
+ if (member.disabled) log(chalk.bold("Disabled: ") + ` ${member.disabled}`);
31
27
  log();
32
28
  };
33
29
  main();
34
-
35
- //#endregion
30
+ //#endregion
31
+ export {};
@@ -0,0 +1,151 @@
1
+ #!/usr/bin/env node
2
+ import client from "../lib/client.js";
3
+ import spinner from "../lib/spinner.js";
4
+ import { Command } from "commander";
5
+ import os from "os";
6
+ import { exec } from "child_process";
7
+ import chalk from "chalk";
8
+ //#region src/bin/short-objective.ts
9
+ const spin = spinner();
10
+ const log = console.log;
11
+ const program = new Command().usage("[command] [options]").description("view, create, or update objectives");
12
+ program.command("view <id>").description("view an objective by id").option("-O, --open", "Open objective in browser").action(viewObjective);
13
+ program.command("create").description("create a new objective").option("-n, --name [text]", "Set name of objective, required", "").option("-d, --description [text]", "Set description of objective", "").option("-s, --state [name]", "Set state of objective (to do, in progress, done)", "").option("--started-at [date]", "Set started override (ISO date or YYYY-MM-DD)", "").option("--completed-at [date]", "Set completed override (ISO date or YYYY-MM-DD)", "").option("-I, --idonly", "Print only ID of objective result").option("-O, --open", "Open objective in browser").action(createObjective);
14
+ program.command("update <id>").description("update an existing objective").option("-n, --name [text]", "Set name of objective", "").option("-d, --description [text]", "Set description of objective", "").option("-s, --state [name]", "Set state of objective (to do, in progress, done)", "").option("--started-at [date]", "Set started override (ISO date or YYYY-MM-DD)", "").option("--completed-at [date]", "Set completed override (ISO date or YYYY-MM-DD)", "").option("-a, --archived", "Archive objective").option("-O, --open", "Open objective in browser").action(updateObjective);
15
+ program.command("epics <id>").description("list epics in an objective").action(listObjectiveEpics);
16
+ const args = process.argv.slice(2);
17
+ if (args.length > 0 && args[0] && /^\d+$/.test(args[0])) process.argv.splice(2, 0, "view");
18
+ program.parse(process.argv);
19
+ if (args.length === 0) {
20
+ program.outputHelp();
21
+ process.exit(1);
22
+ }
23
+ async function viewObjective(id, options) {
24
+ spin.start();
25
+ try {
26
+ const objective = await client.getObjective(parseInt(id, 10)).then((r) => r.data);
27
+ spin.stop(true);
28
+ printObjective(objective);
29
+ if (options.open) openURL(objective.app_url);
30
+ } catch (e) {
31
+ spin.stop(true);
32
+ const error = e;
33
+ if (error.response?.status === 404) log(`Objective #${id} not found`);
34
+ else log("Error fetching objective:", error.message ?? String(e));
35
+ process.exit(1);
36
+ }
37
+ }
38
+ async function createObjective(options) {
39
+ if (!options.name) {
40
+ log("Must provide --name");
41
+ process.exit(1);
42
+ }
43
+ if (!options.idonly) spin.start();
44
+ const objectiveData = { name: options.name };
45
+ if (options.description) objectiveData.description = options.description;
46
+ const state = normalizeObjectiveState(options.state);
47
+ if (state) objectiveData.state = state;
48
+ if (options.startedAt) objectiveData.started_at_override = normalizeDate(options.startedAt);
49
+ if (options.completedAt) objectiveData.completed_at_override = normalizeDate(options.completedAt);
50
+ let objective;
51
+ try {
52
+ objective = await client.createObjective(objectiveData).then((r) => r.data);
53
+ } catch (e) {
54
+ if (!options.idonly) spin.stop(true);
55
+ log("Error creating objective:", e.message ?? String(e));
56
+ process.exit(1);
57
+ }
58
+ if (!options.idonly) spin.stop(true);
59
+ if (options.idonly) {
60
+ log(objective.id);
61
+ return;
62
+ }
63
+ printObjective(objective);
64
+ if (options.open) openURL(objective.app_url);
65
+ }
66
+ async function updateObjective(id, options) {
67
+ const updateData = {};
68
+ if (options.name) updateData.name = options.name;
69
+ if (options.description) updateData.description = options.description;
70
+ const state = normalizeObjectiveState(options.state);
71
+ if (state) updateData.state = state;
72
+ if (options.startedAt) updateData.started_at_override = normalizeDate(options.startedAt);
73
+ if (options.completedAt) updateData.completed_at_override = normalizeDate(options.completedAt);
74
+ if (options.archived) updateData.archived = true;
75
+ if (Object.keys(updateData).length === 0) {
76
+ log("No updates provided. Use --name, --description, --state, --started-at, --completed-at, or --archived");
77
+ process.exit(1);
78
+ }
79
+ spin.start();
80
+ try {
81
+ const objective = await client.updateObjective(parseInt(id, 10), updateData).then((r) => r.data);
82
+ spin.stop(true);
83
+ printObjective(objective);
84
+ if (options.open) openURL(objective.app_url);
85
+ } catch (e) {
86
+ spin.stop(true);
87
+ const error = e;
88
+ if (error.response?.status === 404) log(`Objective #${id} not found`);
89
+ else log("Error updating objective:", error.message ?? String(e));
90
+ process.exit(1);
91
+ }
92
+ }
93
+ async function listObjectiveEpics(id) {
94
+ spin.start();
95
+ try {
96
+ const epics = await client.listObjectiveEpics(parseInt(id, 10)).then((r) => r.data);
97
+ spin.stop(true);
98
+ if (epics.length === 0) {
99
+ log(`No epics found in objective #${id}`);
100
+ return;
101
+ }
102
+ log(chalk.bold(`Epics in objective #${id}:`));
103
+ log();
104
+ epics.forEach(printEpic);
105
+ } catch (e) {
106
+ spin.stop(true);
107
+ const error = e;
108
+ if (error.response?.status === 404) log(`Objective #${id} not found`);
109
+ else log("Error fetching objective epics:", error.message ?? String(e));
110
+ process.exit(1);
111
+ }
112
+ }
113
+ function normalizeObjectiveState(state) {
114
+ if (!state) return void 0;
115
+ const stateMap = {
116
+ todo: "to do",
117
+ "to do": "to do",
118
+ inprogress: "in progress",
119
+ "in progress": "in progress",
120
+ done: "done"
121
+ };
122
+ return stateMap[state.toLowerCase().replace(/[^a-z]/g, "")] || stateMap[state.toLowerCase()];
123
+ }
124
+ function normalizeDate(value) {
125
+ if (/^\d{4}-\d{2}-\d{2}$/.test(value)) return (/* @__PURE__ */ new Date(`${value}T00:00:00.000Z`)).toISOString();
126
+ return new Date(value).toISOString();
127
+ }
128
+ function printObjective(objective) {
129
+ log(`#${objective.id} ${objective.name}`);
130
+ log(`State:\t\t${objective.state}`);
131
+ log(`Started:\t${objective.started ? "yes" : "no"}`);
132
+ log(`Completed:\t${objective.completed ? "yes" : "no"}`);
133
+ log(`Archived:\t${objective.archived ? "yes" : "no"}`);
134
+ if (objective.description) log(`Description:\t${objective.description}`);
135
+ if (objective.categories.length > 0) log(`Categories:\t${objective.categories.map((category) => category.name).join(", ")}`);
136
+ if (objective.started_at) log(`Started At:\t${objective.started_at}`);
137
+ if (objective.completed_at) log(`Completed At:\t${objective.completed_at}`);
138
+ log(`Updated:\t${objective.updated_at}`);
139
+ log(`URL:\t\t${objective.app_url}`);
140
+ }
141
+ function printEpic(epic) {
142
+ log(`${chalk.bold("#" + epic.id)} ${chalk.blue(epic.name)}`);
143
+ log(` State: ${epic.state} | Started: ${epic.started ? "yes" : "no"} | Completed: ${epic.completed ? "yes" : "no"}`);
144
+ log(` URL: ${epic.app_url}`);
145
+ log();
146
+ }
147
+ function openURL(url) {
148
+ exec(`${os.platform() === "darwin" ? "open" : "xdg-open"} '${url}'`);
149
+ }
150
+ //#endregion
151
+ export {};
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env node
2
+ import client from "../lib/client.js";
3
+ import spinner from "../lib/spinner.js";
4
+ import { Command } from "commander";
5
+ import chalk from "chalk";
6
+ //#region src/bin/short-objectives.ts
7
+ const log = console.log;
8
+ const spin = spinner();
9
+ const program = new Command().description(`List and search Shortcut objectives. By default, lists all objectives.
10
+ Passing search operators will use the Shortcut objective search API and page through all results.`).allowExcessArguments(true).usage("[options] [SEARCH OPERATORS]").option("-a, --archived", "List only objectives including archived", "").option("-c, --completed", "List only objectives that have been completed", "").option("-d, --detailed", "List more details for each objective", "").option("-f, --format [template]", "Format each objective output by template", "").option("-s, --started", "List objectives that have been started", "").option("-S, --state [state]", "Filter objectives by state", "").option("-t, --title [query]", "Filter objectives with name/title containing query", "");
11
+ program.parse(process.argv);
12
+ const opts = program.opts();
13
+ const main = async () => {
14
+ spin.start();
15
+ let objectives = [];
16
+ try {
17
+ objectives = program.args.length ? await searchObjectives(program.args) : await listObjectives();
18
+ } catch (e) {
19
+ spin.stop(true);
20
+ log("Error fetching objectives:", e.message ?? String(e));
21
+ process.exit(1);
22
+ }
23
+ spin.stop(true);
24
+ const textMatch = new RegExp(opts.title ?? "", "i");
25
+ const stateMatch = new RegExp(opts.state ?? "", "i");
26
+ objectives.filter((objective) => {
27
+ return `${objective.name} ${objective.description ?? ""}`.match(textMatch) && objective.state.match(stateMatch);
28
+ }).forEach(printItem);
29
+ };
30
+ async function listObjectives() {
31
+ return client.listObjectives().then((r) => r.data);
32
+ }
33
+ async function searchObjectives(args) {
34
+ const query = args.join(" ");
35
+ let result = await client.searchObjectives({
36
+ query,
37
+ detail: "full"
38
+ });
39
+ let objectives = result.data.data;
40
+ while (result.data.next) {
41
+ const nextCursor = new URLSearchParams(result.data.next).get("next");
42
+ result = await client.searchObjectives({
43
+ query,
44
+ detail: "full",
45
+ next: nextCursor ?? void 0
46
+ });
47
+ objectives = objectives.concat(result.data.data);
48
+ }
49
+ return objectives;
50
+ }
51
+ const printItem = (objective) => {
52
+ if (objective.archived && !opts.archived) return;
53
+ if (!objective.started && opts.started) return;
54
+ if (!objective.completed && opts.completed) return;
55
+ let defaultFormat = `#%id %t\nState:\t\t%s\nStarted:\t%st\nCompleted:\t%co\n`;
56
+ if (opts.detailed) defaultFormat += `Updated:\t%u\nCategories:\t%cat\nURL:\t\t%url\nDescription:\t%d\n`;
57
+ const format = opts.format || defaultFormat;
58
+ const categories = "categories" in objective ? objective.categories.map((category) => category.name).join(", ") || "_" : "_";
59
+ log(format.replace(/%id/g, chalk.bold(`${objective.id}`)).replace(/%t/g, chalk.blue(`${objective.name}`)).replace(/%st/g, `${objective.started ? "yes" : "no"}`).replace(/%co/g, `${objective.completed ? "yes" : "no"}`).replace(/%s/g, `${objective.state}`).replace(/%u/g, `${objective.updated_at}`).replace(/%cat/g, categories).replace(/%url/g, `${objective.app_url}`).replace(/%d/g, `${objective.description || "_"}`));
60
+ };
61
+ main();
62
+ //#endregion
63
+ export {};
@@ -1,35 +1,31 @@
1
1
  #!/usr/bin/env node
2
- const require_rolldown_runtime = require('../_virtual/rolldown_runtime.js');
3
- const require_lib_spinner = require('../lib/spinner.js');
4
- const require_lib_client = require('../lib/client.js');
5
- let commander = require("commander");
6
- commander = require_rolldown_runtime.__toESM(commander);
7
- let chalk = require("chalk");
8
- chalk = require_rolldown_runtime.__toESM(chalk);
9
-
2
+ import client from "../lib/client.js";
3
+ import spinner from "../lib/spinner.js";
4
+ import { Command } from "commander";
5
+ import chalk from "chalk";
10
6
  //#region src/bin/short-projects.ts
11
- const spin = require_lib_spinner.default();
7
+ const spin = spinner();
12
8
  const log = console.log;
13
- const program = commander.default.description("Display projects available for stories").option("-a, --archived", "List only projects including archived", "").option("-d, --detailed", "List more details for each project", "").option("-t, --title [query]", "List projects with name/title containing query", "").parse(process.argv);
9
+ const opts = new Command().description("Display projects available for stories").option("-a, --archived", "List only projects including archived", "").option("-d, --detailed", "List more details for each project", "").option("-t, --title [query]", "List projects with name/title containing query", "").parse(process.argv).opts();
14
10
  const main = async () => {
15
11
  spin.start();
16
- const projects = await require_lib_client.default.listProjects().then((r) => r.data);
12
+ const projects = await client.listProjects().then((r) => r.data);
17
13
  spin.stop(true);
18
- const textMatch = new RegExp(program.title, "i");
14
+ const textMatch = new RegExp(opts.title ?? "", "i");
19
15
  projects.filter((o) => {
20
16
  return !!`${o.name} ${o.name}`.match(textMatch);
21
17
  }).map(printItem);
22
18
  };
23
19
  const printItem = (proj) => {
24
- if (proj.archived && !program.archived) return;
25
- log(chalk.default.bold(`#${proj.id}`) + chalk.default.blue(` ${proj.name}`));
26
- log(chalk.default.bold("Points: ") + ` ${proj.stats.num_points}`);
27
- log(chalk.default.bold("Stories: ") + ` ${proj.stats.num_stories}`);
28
- log(chalk.default.bold("Started: ") + ` ${proj.start_time}`);
29
- if (proj.archived) log(chalk.default.bold("Archived: ") + ` ${proj.archived}`);
30
- if (program.detailed) log(chalk.default.bold("Description: ") + ` ${proj.description}`);
20
+ if (proj.archived && !opts.archived) return;
21
+ log(chalk.bold(`#${proj.id}`) + chalk.blue(` ${proj.name}`));
22
+ log(chalk.bold("Points: ") + ` ${proj.stats.num_points}`);
23
+ log(chalk.bold("Stories: ") + ` ${proj.stats.num_stories}`);
24
+ log(chalk.bold("Started: ") + ` ${proj.start_time}`);
25
+ if (proj.archived) log(chalk.bold("Archived: ") + ` ${proj.archived}`);
26
+ if (opts.detailed) log(chalk.bold("Description: ") + ` ${proj.description}`);
31
27
  log();
32
28
  };
33
29
  main();
34
-
35
- //#endregion
30
+ //#endregion
31
+ export {};
@@ -1,54 +1,45 @@
1
1
  #!/usr/bin/env node
2
- const require_rolldown_runtime = require('../_virtual/rolldown_runtime.js');
3
- const require_lib_spinner = require('../lib/spinner.js');
4
- const require_lib_configure = require('../lib/configure.js');
5
- const require_lib_stories = require('../lib/stories.js');
6
- let commander = require("commander");
7
- commander = require_rolldown_runtime.__toESM(commander);
8
-
2
+ import configure_default from "../lib/configure.js";
3
+ import spinner from "../lib/spinner.js";
4
+ import stories_default from "../lib/stories.js";
5
+ import { Command } from "commander";
9
6
  //#region src/bin/short-search.ts
10
- var _process$argv$;
11
- const spin = require_lib_spinner.default("Finding... %s ");
7
+ const spin = spinner("Finding... %s ");
12
8
  const log = console.log;
13
- const program = commander.default.description(`Search through Shortcut stories. Arguments (non-flag/options) will be
9
+ const program = new Command().description(`Search through Shortcut stories. Arguments (non-flag/options) will be
14
10
  passed to Shortcut story search API as search operators. Passing '%self%' as
15
11
  a search operator argument will be replaced by your mention name. Note that
16
12
  passing search operators and options (e.g. --owner foobar) will use the
17
13
  options as extra filtering in the client.
18
14
 
19
15
  Refer to https://help.shortcut.com/hc/en-us/articles/360000046646-Search-Operators
20
- for more details about search operators.`).usage("[options] [SEARCH OPERATORS]").option("-a, --archived", "Include archived Stories").option("-c, --created [operator][date]", "Stories created within criteria (operator is one of <|>|=)", "").option("-q, --quiet", "Print only story output, no loading dialog", "").option("-l, --label [id|name]", "Stories with label id/name, by regex", "").option("-o, --owner [name]", "Stories with owner, by regex", "").option("-p, --project [id]", "Stories in project", "").option("-s, --state [id|name]", "Stories in workflow state id/name, by regex", "").option("--epic [id|name]", "Stories in epic id/name, by regex", "").option("-i, --iteration [id|name]", "Stories in iteration id/name, by regex", "").option("-S, --save [name]", "Save search configuration as workspace").option("-t, --text [name]", "Stories with text in name, by regex", "").option("-e, --estimate [operator][number]", "Stories estimated within criteria (operator is one of <|>|=)", "").option("-u, --updated [operator][date]", "Stories updated within criteria (operator is one of <|>|=)", "").option("-y, --type [name]", "Stories of type, by regex", "").option("-r, --sort [field]", "Sort stories by field (accessor[:asc|desc][,next])", "state.position:asc,position:asc").option("-f, --format [template]", "Format each story output by template", "");
21
- const getWorkspaceOptions = (program$1) => {
22
- const blacklistedKeys = [
23
- "Command",
24
- "commands",
25
- "Option",
26
- "options",
27
- "rawArgs",
28
- "save"
29
- ];
30
- return Object.entries(program$1).filter(([key]) => !(blacklistedKeys.includes(key) || key.startsWith("_"))).reduce((obj, [key, val]) => Object.assign(obj, { [key]: val }), {});
16
+ for more details about search operators.`).allowExcessArguments(true).usage("[options] [SEARCH OPERATORS]").option("-a, --archived", "Include archived Stories").option("-c, --created [operator][date]", "Stories created within criteria (operator is one of <|>|=)", "").option("-q, --quiet", "Print only story output, no loading dialog", "").option("-l, --label [id|name]", "Stories with label id/name, by regex", "").option("-o, --owner [name]", "Stories with owner, by regex", "").option("-p, --project [id]", "Stories in project", "").option("-s, --state [id|name]", "Stories in workflow state id/name, by regex", "").option("--epic [id|name]", "Stories in epic id/name, by regex", "").option("-i, --iteration [id|name]", "Stories in iteration id/name, by regex", "").option("-S, --save [name]", "Save search configuration as workspace").option("-t, --text [name]", "Stories with text in name, by regex", "").option("-e, --estimate [operator][number]", "Stories estimated within criteria (operator is one of <|>|=)", "").option("-u, --updated [operator][date]", "Stories updated within criteria (operator is one of <|>|=)", "").option("-y, --type [name]", "Stories of type, by regex", "").option("-r, --sort [field]", "Sort stories by field (accessor[:asc|desc][,next])", "state.position:asc,position:asc").option("-f, --format [template]", "Format each story output by template", "");
17
+ const getWorkspaceOptions = (opts) => {
18
+ const blacklistedKeys = ["save"];
19
+ return Object.entries(opts).filter(([key]) => !blacklistedKeys.includes(key)).reduce((obj, [key, val]) => Object.assign(obj, { [key]: val }), {});
31
20
  };
32
21
  const main = async () => {
33
22
  program.parse(process.argv);
34
- if (!program.quiet) {
23
+ const opts = program.opts();
24
+ if (!opts.quiet) {
35
25
  if (!program.args.length) log("Fetching all stories for search since no search operators were passed ...");
36
26
  spin.start();
37
27
  }
38
28
  let stories = [];
39
29
  try {
40
- stories = await require_lib_stories.default.listStories(program);
30
+ stories = await stories_default.listStories({
31
+ ...opts,
32
+ args: program.args
33
+ });
41
34
  } catch (e) {
42
35
  log("Error fetching stories:", e);
43
36
  }
44
- if (!program.quiet) spin.stop(true);
45
- stories.map(require_lib_stories.default.printFormattedStory(program));
46
- if (!program.save) return;
47
- const name = program.save === true ? "default" : program.save;
48
- if (require_lib_configure.default.saveWorkspace(name, getWorkspaceOptions(program))) log("Saved query as %s workspace", name);
37
+ if (!opts.quiet) spin.stop(true);
38
+ stories.map(stories_default.printFormattedStory(opts));
39
+ if (!opts.save) return;
40
+ const name = opts.save === true ? "default" : opts.save;
41
+ if (configure_default.saveWorkspace(name, getWorkspaceOptions(opts))) log("Saved query as %s workspace", name);
49
42
  };
50
- if ((_process$argv$ = process.argv[1]) === null || _process$argv$ === void 0 ? void 0 : _process$argv$.includes("short-search")) main();
51
-
43
+ if (process.argv[1]?.includes("short-search")) main();
52
44
  //#endregion
53
- exports.main = main;
54
- exports.program = program;
45
+ export { main, program };