@shortcut-cli/shortcut-cli 3.8.1 → 4.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.
- package/README.md +121 -2
- package/build/bin/{short-api.js → short-api.cjs} +12 -10
- package/build/bin/short-create.cjs +58 -0
- package/build/bin/{short-doc.js → short-doc.cjs} +8 -9
- package/build/bin/{short-docs.js → short-docs.cjs} +18 -19
- package/build/bin/{short-epic.js → short-epic.cjs} +8 -9
- package/build/bin/short-epics.cjs +36 -0
- package/build/bin/{short-find.js → short-find.cjs} +1 -1
- package/build/bin/{short-install.js → short-install.cjs} +8 -9
- package/build/bin/short-iteration.cjs +184 -0
- package/build/bin/short-iterations.cjs +66 -0
- package/build/bin/{short-members.js → short-members.cjs} +6 -7
- package/build/bin/{short-projects.js → short-projects.cjs} +7 -8
- package/build/bin/{short-search.js → short-search.cjs} +20 -25
- package/build/bin/{short-story.js → short-story.cjs} +47 -46
- package/build/bin/{short-workflows.js → short-workflows.cjs} +5 -6
- package/build/bin/short-workspace.cjs +64 -0
- package/build/bin/short.cjs +10 -0
- package/build/lib/client.cjs +11 -0
- package/build/lib/{configure.js → configure.cjs} +3 -3
- package/build/lib/{spinner.js → spinner.cjs} +1 -1
- package/build/lib/{stories.js → stories.cjs} +73 -69
- package/build/{package.js → package.cjs} +1 -1
- package/package.json +67 -67
- package/build/bin/short-create.js +0 -56
- package/build/bin/short-epics.js +0 -37
- package/build/bin/short-workspace.js +0 -64
- package/build/bin/short.js +0 -11
- package/build/lib/client.js +0 -11
- /package/build/_virtual/{rolldown_runtime.js → rolldown_runtime.cjs} +0 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
|
|
3
|
+
const require_lib_spinner = require('../lib/spinner.cjs');
|
|
4
|
+
const require_lib_configure = require('../lib/configure.cjs');
|
|
5
|
+
const require_lib_client = require('../lib/client.cjs');
|
|
6
|
+
const require_lib_stories = require('../lib/stories.cjs');
|
|
7
|
+
let commander = require("commander");
|
|
8
|
+
let child_process = require("child_process");
|
|
9
|
+
let chalk = require("chalk");
|
|
10
|
+
chalk = require_rolldown_runtime.__toESM(chalk);
|
|
11
|
+
|
|
12
|
+
//#region src/bin/short-iteration.ts
|
|
13
|
+
const config = require_lib_configure.loadConfig();
|
|
14
|
+
const spin = require_lib_spinner.default();
|
|
15
|
+
const log = console.log;
|
|
16
|
+
const program = new commander.Command().usage("[command] [options]").description("view, create, update, or delete iterations");
|
|
17
|
+
program.command("view <id>").description("view an iteration by id").option("-O, --open", "Open iteration in browser").action(viewIteration);
|
|
18
|
+
program.command("create").description("create a new iteration").option("-n, --name [text]", "Set name of iteration, required", "").option("-d, --description [text]", "Set description of iteration", "").option("--start-date [date]", "Set start date (YYYY-MM-DD), required", "").option("--end-date [date]", "Set end date (YYYY-MM-DD), required", "").option("-T, --team [id|name]", "Set team/group of iteration", "").option("-I, --idonly", "Print only ID of iteration result").option("-O, --open", "Open iteration in browser").action(createIteration);
|
|
19
|
+
program.command("update <id>").description("update an existing iteration").option("-n, --name [text]", "Set name of iteration", "").option("-d, --description [text]", "Set description of iteration", "").option("--start-date [date]", "Set start date (YYYY-MM-DD)", "").option("--end-date [date]", "Set end date (YYYY-MM-DD)", "").option("-T, --team [id|name]", "Set team/group of iteration", "").option("-O, --open", "Open iteration in browser").action(updateIteration);
|
|
20
|
+
program.command("delete <id>").description("delete an iteration").action(deleteIteration);
|
|
21
|
+
program.command("stories <id>").description("list stories in an iteration").option("-f, --format [template]", "Format each story output by template", "").action(listIterationStories);
|
|
22
|
+
program.parse(process.argv);
|
|
23
|
+
async function viewIteration(id, options) {
|
|
24
|
+
spin.start();
|
|
25
|
+
try {
|
|
26
|
+
const iteration = await require_lib_client.default.getIteration(parseInt(id, 10)).then((r) => r.data);
|
|
27
|
+
spin.stop(true);
|
|
28
|
+
printIteration(iteration);
|
|
29
|
+
if (options.open) (0, child_process.exec)(`open https://app.shortcut.com/${config.urlSlug}/iteration/${iteration.id}`);
|
|
30
|
+
} catch (e) {
|
|
31
|
+
spin.stop(true);
|
|
32
|
+
const error = e;
|
|
33
|
+
if (error.response?.status === 404) log(`Iteration #${id} not found`);
|
|
34
|
+
else log("Error fetching iteration:", error.message ?? String(e));
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
async function createIteration(options) {
|
|
39
|
+
const entities = await require_lib_stories.default.fetchEntities();
|
|
40
|
+
if (!options.idonly) spin.start();
|
|
41
|
+
const iterationData = {
|
|
42
|
+
name: options.name || "",
|
|
43
|
+
start_date: options.startDate || "",
|
|
44
|
+
end_date: options.endDate || ""
|
|
45
|
+
};
|
|
46
|
+
if (options.description) iterationData.description = options.description;
|
|
47
|
+
if (options.team) {
|
|
48
|
+
const group = require_lib_stories.default.findGroup(entities, options.team);
|
|
49
|
+
if (group?.id) iterationData.group_ids = [group.id];
|
|
50
|
+
}
|
|
51
|
+
if (!iterationData.name) {
|
|
52
|
+
if (!options.idonly) spin.stop(true);
|
|
53
|
+
log("Must provide --name");
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
if (!iterationData.start_date) {
|
|
57
|
+
if (!options.idonly) spin.stop(true);
|
|
58
|
+
log("Must provide --start-date");
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
if (!iterationData.end_date) {
|
|
62
|
+
if (!options.idonly) spin.stop(true);
|
|
63
|
+
log("Must provide --end-date");
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
let iteration;
|
|
67
|
+
try {
|
|
68
|
+
iteration = await require_lib_client.default.createIteration(iterationData).then((r) => r.data);
|
|
69
|
+
} catch (e) {
|
|
70
|
+
if (!options.idonly) spin.stop(true);
|
|
71
|
+
log("Error creating iteration:", e.message ?? String(e));
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
if (!options.idonly) spin.stop(true);
|
|
75
|
+
if (iteration) if (options.idonly) log(iteration.id);
|
|
76
|
+
else {
|
|
77
|
+
printIteration(iteration);
|
|
78
|
+
if (options.open) (0, child_process.exec)(`open https://app.shortcut.com/${config.urlSlug}/iteration/${iteration.id}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async function updateIteration(id, options) {
|
|
82
|
+
const entities = await require_lib_stories.default.fetchEntities();
|
|
83
|
+
spin.start();
|
|
84
|
+
const updateData = {};
|
|
85
|
+
if (options.name) updateData.name = options.name;
|
|
86
|
+
if (options.description) updateData.description = options.description;
|
|
87
|
+
if (options.startDate) updateData.start_date = options.startDate;
|
|
88
|
+
if (options.endDate) updateData.end_date = options.endDate;
|
|
89
|
+
if (options.team) {
|
|
90
|
+
const group = require_lib_stories.default.findGroup(entities, options.team);
|
|
91
|
+
if (group?.id) updateData.group_ids = [group.id];
|
|
92
|
+
}
|
|
93
|
+
if (Object.keys(updateData).length === 0) {
|
|
94
|
+
spin.stop(true);
|
|
95
|
+
log("No updates provided. Use --name, --description, --start-date, --end-date, or --team");
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
let iteration;
|
|
99
|
+
try {
|
|
100
|
+
iteration = await require_lib_client.default.updateIteration(parseInt(id, 10), updateData).then((r) => r.data);
|
|
101
|
+
} catch (e) {
|
|
102
|
+
spin.stop(true);
|
|
103
|
+
const error = e;
|
|
104
|
+
if (error.response?.status === 404) log(`Iteration #${id} not found`);
|
|
105
|
+
else log("Error updating iteration:", error.message ?? String(e));
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
spin.stop(true);
|
|
109
|
+
if (iteration) {
|
|
110
|
+
printIteration(iteration);
|
|
111
|
+
if (options.open) (0, child_process.exec)(`open https://app.shortcut.com/${config.urlSlug}/iteration/${iteration.id}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
async function deleteIteration(id) {
|
|
115
|
+
spin.start();
|
|
116
|
+
try {
|
|
117
|
+
await require_lib_client.default.deleteIteration(parseInt(id, 10));
|
|
118
|
+
spin.stop(true);
|
|
119
|
+
log(`Iteration #${id} deleted successfully`);
|
|
120
|
+
} catch (e) {
|
|
121
|
+
spin.stop(true);
|
|
122
|
+
const error = e;
|
|
123
|
+
if (error.response?.status === 404) log(`Iteration #${id} not found`);
|
|
124
|
+
else log("Error deleting iteration:", error.message ?? String(e));
|
|
125
|
+
process.exit(1);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async function listIterationStories(id, options) {
|
|
129
|
+
spin.start();
|
|
130
|
+
try {
|
|
131
|
+
const [stories, entities] = await Promise.all([require_lib_client.default.listIterationStories(parseInt(id, 10)).then((r) => r.data), require_lib_stories.default.fetchEntities()]);
|
|
132
|
+
spin.stop(true);
|
|
133
|
+
if (stories.length === 0) {
|
|
134
|
+
log(`No stories found in iteration #${id}`);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
log(chalk.default.bold(`Stories in iteration #${id}:`));
|
|
138
|
+
log();
|
|
139
|
+
stories.forEach((story) => {
|
|
140
|
+
const hydrated = require_lib_stories.default.hydrateStory(entities, story);
|
|
141
|
+
if (options.format) require_lib_stories.default.printFormattedStory({ format: options.format })(hydrated);
|
|
142
|
+
else {
|
|
143
|
+
const state = hydrated.state?.name ?? "Unknown";
|
|
144
|
+
const owners = hydrated.owners?.map((o) => o?.profile.mention_name).filter(Boolean).join(", ");
|
|
145
|
+
log(`${chalk.default.bold("#" + story.id)} ${chalk.default.blue(story.name)}`);
|
|
146
|
+
log(` Type: ${story.story_type} | State: ${state} | Owners: ${owners || "_"}`);
|
|
147
|
+
log(` Points: ${story.estimate ?? "_"}`);
|
|
148
|
+
log();
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
} catch (e) {
|
|
152
|
+
spin.stop(true);
|
|
153
|
+
const error = e;
|
|
154
|
+
if (error.response?.status === 404) log(`Iteration #${id} not found`);
|
|
155
|
+
else log("Error fetching stories:", error.message ?? String(e));
|
|
156
|
+
process.exit(1);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
function printIteration(iteration) {
|
|
160
|
+
const stats = iteration.stats;
|
|
161
|
+
const totalStories = stats.num_stories_done + stats.num_stories_started + stats.num_stories_unstarted + stats.num_stories_backlog;
|
|
162
|
+
const completionPct = stats.num_points > 0 ? Math.round(stats.num_points_done / stats.num_points * 100) : 0;
|
|
163
|
+
log(chalk.default.blue.bold(`#${iteration.id}`) + chalk.default.blue(` ${iteration.name}`));
|
|
164
|
+
if (iteration.description) log(chalk.default.bold("Description:") + ` ${iteration.description}`);
|
|
165
|
+
log(chalk.default.bold("Status:") + ` ${formatStatus(iteration.status)}`);
|
|
166
|
+
log(chalk.default.bold("Start Date:") + ` ${iteration.start_date}`);
|
|
167
|
+
log(chalk.default.bold("End Date:") + ` ${iteration.end_date}`);
|
|
168
|
+
if (iteration.group_ids && iteration.group_ids.length > 0) log(chalk.default.bold("Teams:") + ` ${iteration.group_ids.join(", ")}`);
|
|
169
|
+
log(chalk.default.bold("Stories:") + ` ${totalStories} (${stats.num_stories_done} done)`);
|
|
170
|
+
log(chalk.default.bold("Points:") + ` ${stats.num_points} (${stats.num_points_done} done)`);
|
|
171
|
+
log(chalk.default.bold("Completion:") + ` ${completionPct}%`);
|
|
172
|
+
log(chalk.default.bold("URL:") + ` ${iteration.app_url}`);
|
|
173
|
+
log();
|
|
174
|
+
}
|
|
175
|
+
function formatStatus(status) {
|
|
176
|
+
switch (status) {
|
|
177
|
+
case "started": return chalk.default.green(status);
|
|
178
|
+
case "done": return chalk.default.gray(status);
|
|
179
|
+
case "unstarted":
|
|
180
|
+
default: return chalk.default.yellow(status);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
//#endregion
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
|
|
3
|
+
const require_lib_spinner = require('../lib/spinner.cjs');
|
|
4
|
+
const require_lib_configure = require('../lib/configure.cjs');
|
|
5
|
+
const require_lib_client = require('../lib/client.cjs');
|
|
6
|
+
let commander = require("commander");
|
|
7
|
+
let chalk = require("chalk");
|
|
8
|
+
chalk = require_rolldown_runtime.__toESM(chalk);
|
|
9
|
+
|
|
10
|
+
//#region src/bin/short-iterations.ts
|
|
11
|
+
const config = require_lib_configure.loadConfig();
|
|
12
|
+
const log = console.log;
|
|
13
|
+
const spin = require_lib_spinner.default();
|
|
14
|
+
const opts = new commander.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();
|
|
15
|
+
const main = async () => {
|
|
16
|
+
spin.start();
|
|
17
|
+
const [iterations, groups] = await Promise.all([require_lib_client.default.listIterations(null).then((r) => r.data), require_lib_client.default.listGroups().then((r) => r.data)]);
|
|
18
|
+
spin.stop(true);
|
|
19
|
+
const textMatch = new RegExp(opts.title ?? "", "i");
|
|
20
|
+
const statusMatch = opts.status ? new RegExp(opts.status, "i") : null;
|
|
21
|
+
const groupsById = new Map(groups.map((g) => [g.id, g]));
|
|
22
|
+
const now = /* @__PURE__ */ new Date();
|
|
23
|
+
iterations.filter((iteration) => {
|
|
24
|
+
if (!iteration.name.match(textMatch)) return false;
|
|
25
|
+
if (statusMatch && !iteration.status.match(statusMatch)) return false;
|
|
26
|
+
if (opts.team) {
|
|
27
|
+
const teamMatch = new RegExp(opts.team, "i");
|
|
28
|
+
if (!iteration.group_ids?.map((id) => groupsById.get(id)).find((g) => g && (g.id.match(teamMatch) || g.name.match(teamMatch)))) return false;
|
|
29
|
+
}
|
|
30
|
+
if (opts.current) {
|
|
31
|
+
const start = new Date(iteration.start_date);
|
|
32
|
+
const end = new Date(iteration.end_date);
|
|
33
|
+
if (now < start || now > end) return false;
|
|
34
|
+
}
|
|
35
|
+
return true;
|
|
36
|
+
}).sort((a, b) => {
|
|
37
|
+
return new Date(b.start_date).getTime() - new Date(a.start_date).getTime();
|
|
38
|
+
}).forEach((iteration) => printItem(iteration, groupsById));
|
|
39
|
+
};
|
|
40
|
+
const printItem = (iteration, groupsById) => {
|
|
41
|
+
const stats = iteration.stats;
|
|
42
|
+
const groups = iteration.group_ids?.map((id) => groupsById.get(id)?.name).filter(Boolean) ?? [];
|
|
43
|
+
const totalStories = stats.num_stories_done + stats.num_stories_started + stats.num_stories_unstarted + stats.num_stories_backlog;
|
|
44
|
+
let defaultFormat = `#%id %t\nStatus:\t\t%s\nStart:\t\t%start\nEnd:\t\t%end\n`;
|
|
45
|
+
defaultFormat += `Teams:\t\t%teams\n`;
|
|
46
|
+
defaultFormat += `Stories:\t%stories (%done done)\nPoints:\t\t%points (%pdone done)\n`;
|
|
47
|
+
if (opts.detailed) {
|
|
48
|
+
defaultFormat += `Completion:\t%completion%\n`;
|
|
49
|
+
defaultFormat += `URL:\t\t%url\n`;
|
|
50
|
+
}
|
|
51
|
+
const format = opts.format || defaultFormat;
|
|
52
|
+
const url = `https://app.shortcut.com/${config.urlSlug}/iteration/${iteration.id}`;
|
|
53
|
+
const completionPct = stats.num_points > 0 ? Math.round(stats.num_points_done / stats.num_points * 100) : 0;
|
|
54
|
+
log(format.replace(/%id/, chalk.default.bold(`${iteration.id}`)).replace(/%t/, chalk.default.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));
|
|
55
|
+
};
|
|
56
|
+
const formatStatus = (status) => {
|
|
57
|
+
switch (status) {
|
|
58
|
+
case "started": return chalk.default.green(status);
|
|
59
|
+
case "done": return chalk.default.gray(status);
|
|
60
|
+
case "unstarted":
|
|
61
|
+
default: return chalk.default.yellow(status);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
main();
|
|
65
|
+
|
|
66
|
+
//#endregion
|
|
@@ -1,27 +1,26 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const require_rolldown_runtime = require('../_virtual/rolldown_runtime.
|
|
3
|
-
const require_lib_spinner = require('../lib/spinner.
|
|
4
|
-
const require_lib_client = require('../lib/client.
|
|
2
|
+
const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
|
|
3
|
+
const require_lib_spinner = require('../lib/spinner.cjs');
|
|
4
|
+
const require_lib_client = require('../lib/client.cjs');
|
|
5
5
|
let commander = require("commander");
|
|
6
|
-
commander = require_rolldown_runtime.__toESM(commander);
|
|
7
6
|
let chalk = require("chalk");
|
|
8
7
|
chalk = require_rolldown_runtime.__toESM(chalk);
|
|
9
8
|
|
|
10
9
|
//#region src/bin/short-members.ts
|
|
11
10
|
const spin = require_lib_spinner.default("Loading... %s ");
|
|
12
11
|
const log = console.log;
|
|
13
|
-
const
|
|
12
|
+
const opts = new commander.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
13
|
const main = async () => {
|
|
15
14
|
spin.start();
|
|
16
15
|
const members = await require_lib_client.default.listMembers(null).then((r) => r.data);
|
|
17
16
|
spin.stop(true);
|
|
18
|
-
const ownerMatch = new RegExp(
|
|
17
|
+
const ownerMatch = new RegExp(opts.search ?? "", "i");
|
|
19
18
|
members.filter((o) => {
|
|
20
19
|
return !!`${o.profile.name} ${o.profile.mention_name}`.match(ownerMatch);
|
|
21
20
|
}).map(printMember);
|
|
22
21
|
};
|
|
23
22
|
const printMember = (member) => {
|
|
24
|
-
if (member.disabled && !
|
|
23
|
+
if (member.disabled && !opts.disabled) return;
|
|
25
24
|
log(chalk.default.bold(`#${member.id}`));
|
|
26
25
|
log(chalk.default.bold("Name: ") + ` ${member.profile.name}`);
|
|
27
26
|
log(chalk.default.bold("Mention Name: ") + ` ${member.profile.mention_name}`);
|
|
@@ -1,33 +1,32 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const require_rolldown_runtime = require('../_virtual/rolldown_runtime.
|
|
3
|
-
const require_lib_spinner = require('../lib/spinner.
|
|
4
|
-
const require_lib_client = require('../lib/client.
|
|
2
|
+
const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
|
|
3
|
+
const require_lib_spinner = require('../lib/spinner.cjs');
|
|
4
|
+
const require_lib_client = require('../lib/client.cjs');
|
|
5
5
|
let commander = require("commander");
|
|
6
|
-
commander = require_rolldown_runtime.__toESM(commander);
|
|
7
6
|
let chalk = require("chalk");
|
|
8
7
|
chalk = require_rolldown_runtime.__toESM(chalk);
|
|
9
8
|
|
|
10
9
|
//#region src/bin/short-projects.ts
|
|
11
10
|
const spin = require_lib_spinner.default();
|
|
12
11
|
const log = console.log;
|
|
13
|
-
const
|
|
12
|
+
const opts = new commander.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
13
|
const main = async () => {
|
|
15
14
|
spin.start();
|
|
16
15
|
const projects = await require_lib_client.default.listProjects().then((r) => r.data);
|
|
17
16
|
spin.stop(true);
|
|
18
|
-
const textMatch = new RegExp(
|
|
17
|
+
const textMatch = new RegExp(opts.title ?? "", "i");
|
|
19
18
|
projects.filter((o) => {
|
|
20
19
|
return !!`${o.name} ${o.name}`.match(textMatch);
|
|
21
20
|
}).map(printItem);
|
|
22
21
|
};
|
|
23
22
|
const printItem = (proj) => {
|
|
24
|
-
if (proj.archived && !
|
|
23
|
+
if (proj.archived && !opts.archived) return;
|
|
25
24
|
log(chalk.default.bold(`#${proj.id}`) + chalk.default.blue(` ${proj.name}`));
|
|
26
25
|
log(chalk.default.bold("Points: ") + ` ${proj.stats.num_points}`);
|
|
27
26
|
log(chalk.default.bold("Stories: ") + ` ${proj.stats.num_stories}`);
|
|
28
27
|
log(chalk.default.bold("Started: ") + ` ${proj.start_time}`);
|
|
29
28
|
if (proj.archived) log(chalk.default.bold("Archived: ") + ` ${proj.archived}`);
|
|
30
|
-
if (
|
|
29
|
+
if (opts.detailed) log(chalk.default.bold("Description: ") + ` ${proj.description}`);
|
|
31
30
|
log();
|
|
32
31
|
};
|
|
33
32
|
main();
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const require_rolldown_runtime = require('../_virtual/rolldown_runtime.
|
|
3
|
-
const require_lib_spinner = require('../lib/spinner.
|
|
4
|
-
const require_lib_configure = require('../lib/configure.
|
|
5
|
-
const require_lib_stories = require('../lib/stories.
|
|
2
|
+
const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
|
|
3
|
+
const require_lib_spinner = require('../lib/spinner.cjs');
|
|
4
|
+
const require_lib_configure = require('../lib/configure.cjs');
|
|
5
|
+
const require_lib_stories = require('../lib/stories.cjs');
|
|
6
6
|
let commander = require("commander");
|
|
7
|
-
commander = require_rolldown_runtime.__toESM(commander);
|
|
8
7
|
|
|
9
8
|
//#region src/bin/short-search.ts
|
|
10
|
-
var _process$argv$;
|
|
11
9
|
const spin = require_lib_spinner.default("Finding... %s ");
|
|
12
10
|
const log = console.log;
|
|
13
|
-
const program = commander.
|
|
11
|
+
const program = new commander.Command().description(`Search through Shortcut stories. Arguments (non-flag/options) will be
|
|
14
12
|
passed to Shortcut story search API as search operators. Passing '%self%' as
|
|
15
13
|
a search operator argument will be replaced by your mention name. Note that
|
|
16
14
|
passing search operators and options (e.g. --owner foobar) will use the
|
|
@@ -18,36 +16,33 @@ const program = commander.default.description(`Search through Shortcut stories.
|
|
|
18
16
|
|
|
19
17
|
Refer to https://help.shortcut.com/hc/en-us/articles/360000046646-Search-Operators
|
|
20
18
|
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 = (
|
|
22
|
-
const blacklistedKeys = [
|
|
23
|
-
|
|
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 }), {});
|
|
19
|
+
const getWorkspaceOptions = (opts) => {
|
|
20
|
+
const blacklistedKeys = ["save"];
|
|
21
|
+
return Object.entries(opts).filter(([key]) => !blacklistedKeys.includes(key)).reduce((obj, [key, val]) => Object.assign(obj, { [key]: val }), {});
|
|
31
22
|
};
|
|
32
23
|
const main = async () => {
|
|
33
24
|
program.parse(process.argv);
|
|
34
|
-
|
|
25
|
+
const opts = program.opts();
|
|
26
|
+
if (!opts.quiet) {
|
|
35
27
|
if (!program.args.length) log("Fetching all stories for search since no search operators were passed ...");
|
|
36
28
|
spin.start();
|
|
37
29
|
}
|
|
38
30
|
let stories = [];
|
|
39
31
|
try {
|
|
40
|
-
stories = await require_lib_stories.default.listStories(
|
|
32
|
+
stories = await require_lib_stories.default.listStories({
|
|
33
|
+
...opts,
|
|
34
|
+
args: program.args
|
|
35
|
+
});
|
|
41
36
|
} catch (e) {
|
|
42
37
|
log("Error fetching stories:", e);
|
|
43
38
|
}
|
|
44
|
-
if (!
|
|
45
|
-
stories.map(require_lib_stories.default.printFormattedStory(
|
|
46
|
-
if (!
|
|
47
|
-
const name =
|
|
48
|
-
if (require_lib_configure.default.saveWorkspace(name, getWorkspaceOptions(
|
|
39
|
+
if (!opts.quiet) spin.stop(true);
|
|
40
|
+
stories.map(require_lib_stories.default.printFormattedStory(opts));
|
|
41
|
+
if (!opts.save) return;
|
|
42
|
+
const name = opts.save === true ? "default" : opts.save;
|
|
43
|
+
if (require_lib_configure.default.saveWorkspace(name, getWorkspaceOptions(opts))) log("Saved query as %s workspace", name);
|
|
49
44
|
};
|
|
50
|
-
if (
|
|
45
|
+
if (process.argv[1]?.includes("short-search")) main();
|
|
51
46
|
|
|
52
47
|
//#endregion
|
|
53
48
|
exports.main = main;
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const require_rolldown_runtime = require('../_virtual/rolldown_runtime.
|
|
3
|
-
const require_lib_spinner = require('../lib/spinner.
|
|
4
|
-
const require_lib_configure = require('../lib/configure.
|
|
5
|
-
const require_lib_client = require('../lib/client.
|
|
6
|
-
const require_lib_stories = require('../lib/stories.
|
|
2
|
+
const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
|
|
3
|
+
const require_lib_spinner = require('../lib/spinner.cjs');
|
|
4
|
+
const require_lib_configure = require('../lib/configure.cjs');
|
|
5
|
+
const require_lib_client = require('../lib/client.cjs');
|
|
6
|
+
const require_lib_stories = require('../lib/stories.cjs');
|
|
7
7
|
let commander = require("commander");
|
|
8
|
-
commander = require_rolldown_runtime.__toESM(commander);
|
|
9
8
|
let debug = require("debug");
|
|
10
9
|
debug = require_rolldown_runtime.__toESM(debug);
|
|
11
10
|
let path = require("path");
|
|
@@ -26,35 +25,37 @@ const spin = require_lib_spinner.default();
|
|
|
26
25
|
const log = console.log;
|
|
27
26
|
const logError = console.error;
|
|
28
27
|
const debug$1 = (0, debug.default)("short");
|
|
29
|
-
const program = commander.
|
|
28
|
+
const program = new commander.Command().usage("[options] <id>").description("Update and/or display story details").option("-a, --archived", "Update story as archived").option("-c, --comment [text]", "Add comment to story", "").option("-d, --description [text]", "Update description of story", "").option("-D, --download", "Download all attached files", "").option("--download-dir [path]", "Directory to download files to", ".").option("-e, --estimate [number]", "Update estimate of story", "").option("--epic [id|name]", "Set epic of story").option("-i, --iteration [id|name]", "Set iteration of story").option("-f, --format [template]", "Format the story output by template", "").option("--from-git", "Fetch story parsed by ID from current git branch").option("--git-branch", "Checkout git branch from story slug <mention-name>/ch<id>/<type>-<title>\n as required by the Git integration: https://bit.ly/2RKO1FF").option("--git-branch-short", "Checkout git branch from story slug <mention-name>/ch<id>/<title>").option("-I, --idonly", "Print only ID of story results", "").option("-l, --label [id|name]", "Stories with label id/name, by regex", "").option("--move-after [id]", "Move story to position below story ID").option("--move-before [id]", "Move story to position above story ID").option("--move-down [n]", "Move story position downward by n stories").option("--move-up [n]", "Move story position upward by n stories").option("-o, --owners [id|name]", "Update owners of story, comma-separated", "").option("-O, --open", "Open story in browser").option("--oe, --open-epic", "Open story's epic in browser").option("--oi, --open-iteration", "Open story's iteration in browser").option("--op, --open-project", "Open story's project in browser").option("-q, --quiet", "Print only story output, no loading dialog", "").option("-s, --state [id|name]", "Update workflow state of story", "").option("-t, --title [text]", "Update title/name of story", "").option("-T, --team [id|name]", "Update team/group of story", "").option("--task [text]", "Create new task on story").option("--task-complete [text]", "Toggle completion of task on story matching text").option("-y, --type [name]", "Update type of story", "").parse(process.argv);
|
|
29
|
+
const opts = program.opts();
|
|
30
30
|
const main = async () => {
|
|
31
31
|
const entities = await require_lib_stories.default.fetchEntities();
|
|
32
|
-
if (!(
|
|
32
|
+
if (!(opts.idonly || opts.quiet)) spin.start();
|
|
33
33
|
debug$1("constructing story update");
|
|
34
34
|
const update = {};
|
|
35
|
-
if (
|
|
36
|
-
if (
|
|
37
|
-
if (
|
|
38
|
-
if (
|
|
39
|
-
if (
|
|
40
|
-
if (
|
|
41
|
-
const
|
|
42
|
-
update.story_type = [
|
|
35
|
+
if (opts.archived) update.archived = true;
|
|
36
|
+
if (opts.state) update.workflow_state_id = require_lib_stories.default.findState(entities, opts.state)?.id;
|
|
37
|
+
if (opts.estimate) update.estimate = parseInt(opts.estimate, 10);
|
|
38
|
+
if (opts.title) update.name = opts.title;
|
|
39
|
+
if (opts.description) update.description = `${opts.description}`;
|
|
40
|
+
if (opts.type) {
|
|
41
|
+
const storyTypes = [
|
|
43
42
|
"feature",
|
|
44
43
|
"bug",
|
|
45
44
|
"chore"
|
|
46
|
-
]
|
|
45
|
+
];
|
|
46
|
+
const typeMatch = new RegExp(opts.type, "i");
|
|
47
|
+
update.story_type = storyTypes.find((t) => t.match(typeMatch));
|
|
47
48
|
}
|
|
48
|
-
if (
|
|
49
|
-
if (
|
|
50
|
-
if (
|
|
51
|
-
if (
|
|
52
|
-
if (
|
|
53
|
-
const hasPositionUpdate =
|
|
49
|
+
if (opts.owners) update.owner_ids = require_lib_stories.default.findOwnerIds(entities, opts.owners);
|
|
50
|
+
if (opts.epic) update.epic_id = require_lib_stories.default.findEpic(entities, opts.epic)?.id;
|
|
51
|
+
if (opts.iteration) update.iteration_id = require_lib_stories.default.findIteration(entities, opts.iteration)?.id;
|
|
52
|
+
if (opts.label) update.labels = require_lib_stories.default.findLabelNames(entities, opts.label);
|
|
53
|
+
if (opts.team) update.group_id = require_lib_stories.default.findGroup(entities, opts.team)?.id;
|
|
54
|
+
const hasPositionUpdate = opts.moveAfter !== void 0 || opts.moveBefore !== void 0 || opts.moveDown !== void 0 || opts.moveUp !== void 0;
|
|
54
55
|
const hasUpdate = Object.keys(update).length > 0 || hasPositionUpdate;
|
|
55
56
|
debug$1("constructed story update", update);
|
|
56
57
|
const gitID = [];
|
|
57
|
-
if (
|
|
58
|
+
if (opts.fromGit || !program.args.length) {
|
|
58
59
|
debug$1("fetching story ID from git");
|
|
59
60
|
let branch = "";
|
|
60
61
|
try {
|
|
@@ -77,9 +78,9 @@ const main = async () => {
|
|
|
77
78
|
const id = parseInt(_id, 10);
|
|
78
79
|
let story;
|
|
79
80
|
try {
|
|
80
|
-
if (
|
|
81
|
+
if (opts.comment) {
|
|
81
82
|
debug$1("request comment create");
|
|
82
|
-
await require_lib_client.default.createStoryComment(id, { text:
|
|
83
|
+
await require_lib_client.default.createStoryComment(id, { text: opts.comment });
|
|
83
84
|
debug$1("response comment create");
|
|
84
85
|
}
|
|
85
86
|
} catch (e) {
|
|
@@ -88,9 +89,9 @@ const main = async () => {
|
|
|
88
89
|
process.exit(3);
|
|
89
90
|
}
|
|
90
91
|
try {
|
|
91
|
-
if (
|
|
92
|
+
if (opts.task) {
|
|
92
93
|
debug$1("request task create");
|
|
93
|
-
await require_lib_client.default.createTask(id, { description:
|
|
94
|
+
await require_lib_client.default.createTask(id, { description: opts.task });
|
|
94
95
|
debug$1("response task create");
|
|
95
96
|
}
|
|
96
97
|
} catch (e) {
|
|
@@ -108,9 +109,9 @@ const main = async () => {
|
|
|
108
109
|
process.exit(4);
|
|
109
110
|
}
|
|
110
111
|
try {
|
|
111
|
-
if (
|
|
112
|
+
if (opts.taskComplete) {
|
|
112
113
|
debug$1("calculating task(s) to complete");
|
|
113
|
-
const descMatch = new RegExp(
|
|
114
|
+
const descMatch = new RegExp(opts.taskComplete, "i");
|
|
114
115
|
const tasks = story.tasks.filter((t) => t.description.match(descMatch));
|
|
115
116
|
const updatedTaskIds = tasks.map((t) => t.id);
|
|
116
117
|
debug$1("request tasks complete", updatedTaskIds);
|
|
@@ -136,10 +137,10 @@ const main = async () => {
|
|
|
136
137
|
});
|
|
137
138
|
const siblingIds = siblings.map((s) => s.id);
|
|
138
139
|
const storyIndex = siblingIds.indexOf(~~id);
|
|
139
|
-
if (
|
|
140
|
-
else if (
|
|
141
|
-
else if (
|
|
142
|
-
else if (
|
|
140
|
+
if (opts.moveAfter) update.after_id = ~~opts.moveAfter;
|
|
141
|
+
else if (opts.moveBefore) update.before_id = ~~opts.moveBefore;
|
|
142
|
+
else if (opts.moveUp) update.before_id = siblingIds[Math.max(0, storyIndex - (~~opts.moveUp || 1))];
|
|
143
|
+
else if (opts.moveDown) update.after_id = siblingIds[Math.min(siblings.length - 1, storyIndex + (~~opts.moveDown || 1))];
|
|
143
144
|
debug$1("constructed story position update", update);
|
|
144
145
|
}
|
|
145
146
|
debug$1("request story update");
|
|
@@ -153,28 +154,28 @@ const main = async () => {
|
|
|
153
154
|
process.exit(5);
|
|
154
155
|
}
|
|
155
156
|
if (story) story = require_lib_stories.default.hydrateStory(entities, story);
|
|
156
|
-
if (!
|
|
157
|
+
if (!opts.idonly) spin.stop(true);
|
|
157
158
|
if (story) {
|
|
158
159
|
printStory(story, entities);
|
|
159
|
-
if (
|
|
160
|
-
if (
|
|
160
|
+
if (opts.open) openURL(require_lib_stories.default.storyURL(story));
|
|
161
|
+
if (opts.openEpic) {
|
|
161
162
|
if (!story.epic_id) {
|
|
162
163
|
logError("This story is not part of an epic.");
|
|
163
164
|
process.exit(21);
|
|
164
165
|
}
|
|
165
166
|
openURL(require_lib_stories.default.buildURL("epic", story.epic_id));
|
|
166
167
|
}
|
|
167
|
-
if (
|
|
168
|
+
if (opts.openIteration) {
|
|
168
169
|
if (!story.iteration_id) {
|
|
169
170
|
logError("This story is not part of an iteration.");
|
|
170
171
|
process.exit(22);
|
|
171
172
|
}
|
|
172
173
|
openURL(require_lib_stories.default.buildURL("iteration", story.iteration_id));
|
|
173
174
|
}
|
|
174
|
-
if (
|
|
175
|
+
if (opts.openProject) openURL(require_lib_stories.default.buildURL("project", story.project_id));
|
|
175
176
|
}
|
|
176
|
-
if (
|
|
177
|
-
if (story &&
|
|
177
|
+
if (opts.download) downloadFiles(story);
|
|
178
|
+
if (story && opts.gitBranch) {
|
|
178
179
|
if (!config.mentionName) {
|
|
179
180
|
stopSpinner();
|
|
180
181
|
require_lib_stories.default.checkoutStoryBranch(story, `${story.story_type}-${story.id}-`);
|
|
@@ -183,7 +184,7 @@ const main = async () => {
|
|
|
183
184
|
process.exit(10);
|
|
184
185
|
}
|
|
185
186
|
require_lib_stories.default.checkoutStoryBranch(story);
|
|
186
|
-
} else if (story &&
|
|
187
|
+
} else if (story && opts.gitBranchShort) require_lib_stories.default.checkoutStoryBranch(story, `${config.mentionName}/sc-${story.id}/`);
|
|
187
188
|
});
|
|
188
189
|
stopSpinner();
|
|
189
190
|
};
|
|
@@ -191,11 +192,11 @@ const openURL = (url) => {
|
|
|
191
192
|
(0, child_process.execSync)(`${os.default.platform() === "darwin" ? "open" : "xdg-open"} '${url}'`);
|
|
192
193
|
};
|
|
193
194
|
const stopSpinner = () => {
|
|
194
|
-
if (!(
|
|
195
|
+
if (!(opts.idonly || opts.quiet)) spin.stop(true);
|
|
195
196
|
};
|
|
196
197
|
const downloadFiles = (story) => story.files.map((file) => {
|
|
197
198
|
https.default.get(require_lib_stories.default.fileURL(file), (res) => {
|
|
198
|
-
const filePath = path.default.join(
|
|
199
|
+
const filePath = path.default.join(opts.downloadDir ?? ".", file.name);
|
|
199
200
|
log(chalk.default.bold("Downloading file to: ") + filePath);
|
|
200
201
|
const stream = fs.default.createWriteStream(filePath);
|
|
201
202
|
res.pipe(stream);
|
|
@@ -203,8 +204,8 @@ const downloadFiles = (story) => story.files.map((file) => {
|
|
|
203
204
|
});
|
|
204
205
|
});
|
|
205
206
|
const printStory = (story, entities) => {
|
|
206
|
-
if (
|
|
207
|
-
if (
|
|
207
|
+
if (opts.idonly) return log(story.id);
|
|
208
|
+
if (opts.format) return require_lib_stories.default.printFormattedStory(opts)(story);
|
|
208
209
|
require_lib_stories.default.printDetailedStory(story, entities);
|
|
209
210
|
};
|
|
210
211
|
main();
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const require_rolldown_runtime = require('../_virtual/rolldown_runtime.
|
|
3
|
-
const require_lib_spinner = require('../lib/spinner.
|
|
4
|
-
const require_lib_client = require('../lib/client.
|
|
2
|
+
const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
|
|
3
|
+
const require_lib_spinner = require('../lib/spinner.cjs');
|
|
4
|
+
const require_lib_client = require('../lib/client.cjs');
|
|
5
5
|
let commander = require("commander");
|
|
6
|
-
commander = require_rolldown_runtime.__toESM(commander);
|
|
7
6
|
let chalk = require("chalk");
|
|
8
7
|
chalk = require_rolldown_runtime.__toESM(chalk);
|
|
9
8
|
|
|
10
9
|
//#region src/bin/short-workflows.ts
|
|
11
10
|
const spin = require_lib_spinner.default();
|
|
12
11
|
const log = console.log;
|
|
13
|
-
const
|
|
12
|
+
const opts = new commander.Command().description("Display workflows/states available for stories").option("-s, --search [query]", "List states containing query", "").parse(process.argv).opts();
|
|
14
13
|
const main = async () => {
|
|
15
14
|
spin.start();
|
|
16
15
|
const wfs = await require_lib_client.default.listWorkflows().then((r) => r.data);
|
|
@@ -23,7 +22,7 @@ const printWf = (wf) => {
|
|
|
23
22
|
wf.states.map(printWfState);
|
|
24
23
|
};
|
|
25
24
|
const printWfState = (state) => {
|
|
26
|
-
if (!state.name.match(new RegExp(
|
|
25
|
+
if (!state.name.match(new RegExp(opts.search ?? "", "i"))) return;
|
|
27
26
|
log(chalk.default.bold(` #${state.id}`) + ` ${state.name}`);
|
|
28
27
|
log(` Type: \t${state.type}`);
|
|
29
28
|
log(` Stories:\t${state.num_stories}`);
|