@shortcut-cli/shortcut-cli 3.2.4 → 3.4.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.
@@ -1,138 +1,604 @@
1
1
  #!/usr/bin/env node
2
- "use strict";
3
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
4
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
5
- return new (P || (P = Promise))(function (resolve, reject) {
6
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
7
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
8
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
9
- step((generator = generator.apply(thisArg, _arguments || [])).next());
10
- });
11
- };
12
- var __generator = (this && this.__generator) || function (thisArg, body) {
13
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
14
- return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
15
- function verb(n) { return function (v) { return step([n, v]); }; }
16
- function step(op) {
17
- if (f) throw new TypeError("Generator is already executing.");
18
- while (_) try {
19
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
20
- if (y = 0, t) op = [op[0] & 2, t.value];
21
- switch (op[0]) {
22
- case 0: case 1: t = op; break;
23
- case 4: _.label++; return { value: op[1], done: false };
24
- case 5: _.label++; y = op[1]; op = [0]; continue;
25
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
26
- default:
27
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
28
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
29
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
30
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
31
- if (t[2]) _.ops.pop();
32
- _.trys.pop(); continue;
33
- }
34
- op = body.call(thisArg, _);
35
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
36
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
37
- }
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
38
11
  };
39
- var __read = (this && this.__read) || function (o, n) {
40
- var m = typeof Symbol === "function" && o[Symbol.iterator];
41
- if (!m) return o;
42
- var i = m.call(o), r, ar = [], e;
43
- try {
44
- while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/bin/short-search.ts
31
+ var short_search_exports = {};
32
+ __export(short_search_exports, {
33
+ main: () => main,
34
+ program: () => program
35
+ });
36
+ module.exports = __toCommonJS(short_search_exports);
37
+ var import_commander = __toESM(require("commander"));
38
+
39
+ // src/lib/spinner.ts
40
+ var import_cli_spinner = require("cli-spinner");
41
+ var spinner = (text = "") => {
42
+ const spin2 = new import_cli_spinner.Spinner({
43
+ text: text ? text : "Loading... %s ",
44
+ stream: process.stderr
45
+ });
46
+ spin2.setSpinnerString(27);
47
+ return spin2;
48
+ };
49
+ var spinner_default = spinner;
50
+
51
+ // src/lib/configure.ts
52
+ var import_path = __toESM(require("path"));
53
+ var import_fs = __toESM(require("fs"));
54
+ var import_os = __toESM(require("os"));
55
+ function getConfigDir(suffix) {
56
+ const configBaseDir = process.env.XDG_CONFIG_HOME || import_path.default.resolve(process.env.XDG_DATA_HOME || import_os.default.homedir(), ".config");
57
+ return import_path.default.resolve(configBaseDir, suffix);
58
+ }
59
+ var configDir = getConfigDir("shortcut-cli");
60
+ var configFile = import_path.default.resolve(configDir, "config.json");
61
+ var legacyConfigDirs = [
62
+ getConfigDir("clubhouse-cli"),
63
+ import_path.default.resolve(import_os.default.homedir(), ".clubhouse-cli")
64
+ ];
65
+ var CONFIG_CACHE = null;
66
+ var loadConfig = () => {
67
+ const config3 = loadCachedConfig();
68
+ if (!config3 || config3 === {} || !config3.token) {
69
+ console.error("Please run 'short install' to configure Shortcut API access.");
70
+ process.exit(11);
71
+ }
72
+ if (!config3.urlSlug) {
73
+ console.error(
74
+ "Your config must be updated with data from Shortcut. Please run 'short install --refresh'."
75
+ );
76
+ process.exit(12);
77
+ }
78
+ return config3;
79
+ };
80
+ var loadCachedConfig = () => {
81
+ if (CONFIG_CACHE) {
82
+ return { ...CONFIG_CACHE };
83
+ }
84
+ let config3 = {};
85
+ const token = process.env.SHORTCUT_API_TOKEN || process.env.CLUBHOUSE_API_TOKEN;
86
+ legacyConfigDirs.forEach((dir) => {
87
+ if (import_fs.default.existsSync(dir)) {
88
+ createConfigDir();
89
+ import_fs.default.renameSync(dir, configDir);
45
90
  }
46
- catch (error) { e = { error: error }; }
47
- finally {
48
- try {
49
- if (r && !r.done && (m = i["return"])) m.call(i);
50
- }
51
- finally { if (e) throw e.error; }
91
+ });
92
+ if (import_fs.default.existsSync(configFile)) {
93
+ try {
94
+ config3 = JSON.parse(import_fs.default.readFileSync(configFile, "utf8"));
95
+ } catch (e) {
96
+ console.error(e);
97
+ process.exit(10);
52
98
  }
53
- return ar;
54
- };
55
- Object.defineProperty(exports, "__esModule", { value: true });
56
- exports.main = exports.program = void 0;
57
- var spinner_1 = require("../lib/spinner");
58
- var commander = require("commander");
59
- var configure_1 = require("../lib/configure");
60
- var stories_1 = require("../lib/stories");
61
- var spin = (0, spinner_1.default)('Finding... %s ');
99
+ }
100
+ if (token) {
101
+ config3 = { token, ...config3 };
102
+ }
103
+ CONFIG_CACHE = { ...config3 };
104
+ return config3;
105
+ };
106
+ var createConfigDir = () => {
107
+ const dir = import_path.default.dirname(configDir);
108
+ if (!import_fs.default.existsSync(dir)) {
109
+ import_fs.default.mkdirSync(dir);
110
+ }
111
+ if (!import_fs.default.existsSync(configDir)) {
112
+ import_fs.default.mkdirSync(configDir);
113
+ }
114
+ };
115
+ var saveConfig = (config3) => {
116
+ try {
117
+ createConfigDir();
118
+ import_fs.default.writeFileSync(configFile, JSON.stringify(config3), { flag: "w" });
119
+ CONFIG_CACHE = { ...config3 };
120
+ return true;
121
+ } catch (e) {
122
+ console.error(e);
123
+ return false;
124
+ }
125
+ };
126
+ var updateConfig = (newConfig) => {
127
+ const extantConfig = loadCachedConfig() || {};
128
+ return saveConfig({ ...newConfig, ...extantConfig });
129
+ };
130
+ var saveWorkspace = (name, workspace) => {
131
+ const extantConfig = loadCachedConfig();
132
+ const workspaces = extantConfig.workspaces || {};
133
+ workspaces[name] = workspace;
134
+ return saveConfig({ workspaces, ...extantConfig });
135
+ };
136
+ var removeWorkspace = (name) => {
137
+ const extant = loadCachedConfig();
138
+ delete extant.workspaces[name];
139
+ return saveConfig(Object.assign({}, extant));
140
+ };
141
+ var configure_default = {
142
+ loadConfig,
143
+ updateConfig,
144
+ saveWorkspace,
145
+ removeWorkspace
146
+ };
147
+
148
+ // src/lib/stories.ts
149
+ var import_child_process = require("child_process");
150
+ var import_chalk = __toESM(require("chalk"));
151
+ var import_debug = __toESM(require("debug"));
152
+
153
+ // src/lib/client.ts
154
+ var import_client = require("@shortcut/client");
155
+ var config = loadConfig();
156
+ var client = new import_client.ShortcutClient(config.token);
157
+ var client_default = client;
158
+
159
+ // src/lib/stories.ts
160
+ var debug = (0, import_debug.default)("club");
161
+ var config2 = loadConfig();
62
162
  var log = console.log;
63
- exports.program = commander
64
- .description("Search through Shortcut stories. Arguments (non-flag/options) will be\n passed to Shortcut story search API as search operators. Passing '%self%' as\n a search operator argument will be replaced by your mention name. Note that\n passing search operators and options (e.g. --owner foobar) will use the\n options as extra filtering in the client.\n\n Refer to https://help.shortcut.com/hc/en-us/articles/360000046646-Search-Operators\n for more details about search operators.")
65
- .usage('[options] [SEARCH OPERATORS]')
66
- .option('-a, --archived', 'Include archived Stories')
67
- .option('-c, --created [operator][date]', 'Stories created within criteria (operator is one of <|>|=)', '')
68
- .option('-q, --quiet', 'Print only story output, no loading dialog', '')
69
- .option('-l, --label [id|name]', 'Stories with label id/name, by regex', '')
70
- .option('-o, --owner [name]', 'Stories with owner, by regex', '')
71
- .option('-p, --project [id]', 'Stories in project', '')
72
- .option('-s, --state [id|name]', 'Stories in workflow state id/name, by regex', '')
73
- .option('--epic [id|name]', 'Stories in epic id/name, by regex', '')
74
- .option('-i, --iteration [id|name]', 'Stories in iteration id/name, by regex', '')
75
- .option('-S, --save [name]', 'Save search configuration as workspace')
76
- .option('-t, --text [name]', 'Stories with text in name, by regex', '')
77
- .option('-e, --estimate [operator][number]', 'Stories estimated within criteria (operator is one of <|>|=)', '')
78
- .option('-u, --updated [operator][date]', 'Stories updated within criteria (operator is one of <|>|=)', '')
79
- .option('-y, --type [name]', 'Stories of type, by regex', '')
80
- .option('-r, --sort [field]', 'Sort stories by field (accessor[:asc|desc][,next])', 'state.position:asc,position:asc')
81
- .option('-f, --format [template]', 'Format each story output by template', '');
82
- var getWorkspaceOptions = function (program) {
83
- var blacklistedKeys = ['Command', 'commands', 'Option', 'options', 'rawArgs', 'save'];
84
- return Object.entries(program)
85
- .filter(function (_a) {
86
- var _b = __read(_a, 1), key = _b[0];
87
- return !(blacklistedKeys.includes(key) || key.startsWith('_'));
88
- })
89
- .reduce(function (obj, _a) {
90
- var _b;
91
- var _c = __read(_a, 2), key = _c[0], val = _c[1];
92
- return Object.assign(obj, (_b = {}, _b[key] = val, _b));
93
- }, {});
94
- };
95
- var main = function () { return __awaiter(void 0, void 0, void 0, function () {
96
- var stories, e_1, name;
97
- return __generator(this, function (_a) {
98
- switch (_a.label) {
99
- case 0:
100
- exports.program.parse(process.argv);
101
- if (!exports.program.quiet) {
102
- if (!exports.program.args.length) {
103
- log('Fetching all stories for search since no search operators were passed ...');
104
- }
105
- spin.start();
106
- }
107
- stories = [];
108
- _a.label = 1;
109
- case 1:
110
- _a.trys.push([1, 3, , 4]);
111
- return [4, stories_1.default.listStories(exports.program)];
112
- case 2:
113
- stories = _a.sent();
114
- return [3, 4];
115
- case 3:
116
- e_1 = _a.sent();
117
- log('Error fetching stories:', e_1);
118
- return [3, 4];
119
- case 4:
120
- if (!exports.program.quiet)
121
- spin.stop(true);
122
- stories.map(stories_1.default.printFormattedStory(exports.program));
123
- if (!exports.program.save) {
124
- return [2];
125
- }
126
- name = exports.program.save === true ? 'default' : exports.program.save;
127
- if (configure_1.default.saveWorkspace(name, getWorkspaceOptions(exports.program))) {
128
- log('Saved query as %s workspace', name);
129
- }
130
- return [2];
131
- }
132
- });
133
- }); };
134
- exports.main = main;
135
- if (require.main === module) {
136
- (0, exports.main)();
163
+ async function fetchEntities() {
164
+ const [projectsById, statesById, membersById, groupsById, epicsById, iterationsById, labels] = await Promise.all([
165
+ client_default.listProjects().then((r) => r.data).then(mapByItemId),
166
+ client_default.listWorkflows().then((r) => r.data).then((wfs) => wfs.reduce((states, wf) => states.concat(wf.states), [])).then(mapByItemId),
167
+ client_default.listMembers(null).then((r) => r.data).then(mapByItemStringId),
168
+ client_default.listGroups().then((r) => r.data).then(mapByItemStringId),
169
+ client_default.listEpics(null).then((r) => r.data).then(mapByItemId),
170
+ client_default.listIterations(null).then((r) => r.data).then(mapByItemId),
171
+ client_default.listLabels(null).then((r) => r.data)
172
+ ]).catch((err) => {
173
+ log(`Error fetching workflows: ${err}`);
174
+ process.exit(2);
175
+ });
176
+ debug("response workflows, members, groups, projects, epics, iterations");
177
+ return { projectsById, statesById, membersById, groupsById, epicsById, iterationsById, labels };
178
+ }
179
+ var listStories = async (program2) => {
180
+ debug("request workflows, members, projects, epics");
181
+ const entities = await fetchEntities();
182
+ const stories = await fetchStories(program2, entities);
183
+ debug("filtering stories");
184
+ return filterStories(program2, stories, entities).sort(sortStories(program2));
185
+ };
186
+ function mapByItemId(items) {
187
+ return items.reduce((map, obj) => map.set(obj.id, obj), /* @__PURE__ */ new Map());
188
+ }
189
+ function mapByItemStringId(items) {
190
+ return items.reduce((map, obj) => map.set(obj.id, obj), /* @__PURE__ */ new Map());
191
+ }
192
+ async function fetchStories(program2, entities) {
193
+ if ((program2.args || []).length) {
194
+ debug("using the search endpoint");
195
+ return searchStories(program2);
196
+ }
197
+ debug("filtering projects");
198
+ const regexProject = new RegExp(program2.project, "i");
199
+ const projectIds = [...entities.projectsById.values()].filter(
200
+ (p) => !!(p.id + p.name).match(regexProject)
201
+ );
202
+ debug("request all stories for project(s)", projectIds.map((p) => p.name).join(", "));
203
+ return Promise.all(projectIds.map((p) => client_default.listStories(p.id, null))).then(
204
+ (projectStories) => projectStories.reduce((acc, stories) => acc.concat(stories.data), [])
205
+ );
206
+ }
207
+ async function searchStories(program2) {
208
+ const query = program2.args.join(" ").replace("%self%", config2.mentionName);
209
+ let result = await client_default.searchStories({ query });
210
+ let stories = result.data.data.map(storySearchResultToStory);
211
+ while (result.data.next) {
212
+ const nextCursor = new URLSearchParams(result.data.next).get("next");
213
+ result = await client_default.searchStories({ query, next: nextCursor });
214
+ stories = stories.concat(result.data.data.map(storySearchResultToStory));
215
+ }
216
+ return stories;
217
+ }
218
+ var storySearchResultToStory = (storySearchResult) => {
219
+ return {
220
+ ...storySearchResult,
221
+ description: storySearchResult.description || "",
222
+ linked_files: storySearchResult.linked_files || [],
223
+ comments: storySearchResult.comments || [],
224
+ branches: storySearchResult.branches || [],
225
+ tasks: storySearchResult.tasks || [],
226
+ pull_requests: storySearchResult.pull_requests || [],
227
+ commits: storySearchResult.commits || [],
228
+ files: storySearchResult.files || []
229
+ };
230
+ };
231
+ var hydrateStory = (entities, story) => {
232
+ debug("hydrating story");
233
+ const augmented = story;
234
+ augmented.project = entities.projectsById.get(story.project_id);
235
+ augmented.state = entities.statesById.get(story.workflow_state_id);
236
+ augmented.epic = entities.epicsById.get(story.epic_id);
237
+ augmented.iteration = entities.iterationsById.get(story.iteration_id);
238
+ augmented.owners = story.owner_ids.map((id) => entities.membersById.get(id));
239
+ augmented.requester = entities.membersById.get(story.requested_by_id);
240
+ augmented.group = entities.groupsById.get(story.group_id);
241
+ debug("hydrated story");
242
+ return augmented;
243
+ };
244
+ var isNumber = (val) => !!(val || val === 0) && !isNaN(Number(val.toString()));
245
+ var findEntity = (entities, id) => {
246
+ if (entities.get(id)) {
247
+ return entities.get(id);
248
+ }
249
+ if (isNumber(id) && Number(id.toString())) {
250
+ return entities.get(Number(id.toString()));
251
+ }
252
+ const match = new RegExp(`${id}`, "i");
253
+ return Object.values(entities).filter((s) => !!s.name.match(match))[0];
254
+ };
255
+ var findProject = (entities, project) => findEntity(entities.projectsById, project);
256
+ var findGroup = (entities, group) => findEntity(entities.groupsById, group);
257
+ var findState = (entities, state) => findEntity(entities.statesById, state);
258
+ var findEpic = (entities, epicName) => findEntity(entities.epicsById, epicName);
259
+ var findIteration = (entities, iterationName) => findEntity(entities.statesById, iterationName);
260
+ var findOwnerIds = (entities, owners) => {
261
+ const ownerMatch = new RegExp(owners.split(",").join("|"), "i");
262
+ return Object.values(entities.membersById).filter((m) => !!`${m.id} ${m.profile.name} ${m.profile.mention_name}`.match(ownerMatch)).map((m) => m.id);
263
+ };
264
+ var findLabelNames = (entities, label) => {
265
+ const labelMatch = new RegExp(label.split(",").join("|"), "i");
266
+ return entities.labels.filter((m) => !!`${m.id} ${m.name}`.match(labelMatch)).map((m) => ({ name: m.name }));
267
+ };
268
+ var filterStories = (program2, stories, entities) => {
269
+ let created_at;
270
+ if (program2.created) {
271
+ created_at = parseDateComparator(program2.created);
272
+ }
273
+ let updated_at;
274
+ if (program2.updated) {
275
+ updated_at = parseDateComparator(program2.updated);
276
+ }
277
+ let estimate;
278
+ if (program2.estimate) {
279
+ estimate = parseNumberComparator(program2.estimate);
280
+ }
281
+ const regexLabel = new RegExp(program2.label, "i");
282
+ const regexState = new RegExp(program2.state, "i");
283
+ const regexOwner = new RegExp(program2.owner, "i");
284
+ const regexText = new RegExp(program2.text, "i");
285
+ const regexType = new RegExp(program2.type, "i");
286
+ const regexEpic = new RegExp(program2.epic, "i");
287
+ const regexIteration = new RegExp(program2.iteration, "i");
288
+ return stories.map((story) => hydrateStory(entities, story)).filter((s) => {
289
+ if (!program2.archived && s.archived) {
290
+ return false;
291
+ }
292
+ if (!(s.labels.map((l) => `${l.id},${l.name}`).join(",") + "").match(regexLabel)) {
293
+ return false;
294
+ }
295
+ if (!(s.workflow_state_id + " " + (s.state || {}).name).match(
296
+ regexState
297
+ )) {
298
+ return false;
299
+ }
300
+ if (!(s.epic_id + " " + (s.epic || {}).name).match(regexEpic)) {
301
+ return false;
302
+ }
303
+ if (!(s.iteration_id + " " + (s.iteration || {}).name).match(
304
+ regexIteration
305
+ )) {
306
+ return false;
307
+ }
308
+ if (program2.owner) {
309
+ const owned = s.owners.filter((o) => {
310
+ return !!`${o.profile.name} ${o.profile.mention_name}`.match(regexOwner);
311
+ }).length > 0;
312
+ if (!owned) return false;
313
+ }
314
+ if (!s.name.match(regexText)) {
315
+ return false;
316
+ }
317
+ if (!s.story_type.match(regexType)) {
318
+ return false;
319
+ }
320
+ if (created_at && !created_at(s.created_at)) {
321
+ return false;
322
+ }
323
+ if (updated_at && !updated_at(s.updated_at)) {
324
+ return false;
325
+ }
326
+ return !(estimate && !estimate(s.estimate));
327
+ });
328
+ };
329
+ var sortStories = (program2) => {
330
+ const fields = (program2.sort || "").split(",").map((s) => {
331
+ return s.split(":").map((ss) => ss.split("."));
332
+ });
333
+ const pluck = (acc, val) => {
334
+ if (acc[val] === void 0) return {};
335
+ return acc[val];
336
+ };
337
+ debug("sorting stories");
338
+ return (a, b) => {
339
+ return fields.reduce((acc, field) => {
340
+ if (acc !== 0) return acc;
341
+ const ap = field[0].reduce(pluck, a);
342
+ const bp = field[0].reduce(pluck, b);
343
+ if (ap === bp) return 0;
344
+ const direction = (field[1] || [""])[0].match(/des/i) ? 1 : -1;
345
+ if (ap > bp) {
346
+ if (direction > 0) return -1;
347
+ } else {
348
+ if (direction < 0) return -1;
349
+ }
350
+ return 1;
351
+ }, 0);
352
+ };
353
+ };
354
+ var printFormattedStory = (program2) => {
355
+ return (story) => {
356
+ const defaultFormat = `#%id %t
357
+ Type: %y/%e
358
+ Team: %T
359
+ Project: %p
360
+ Epic: %epic
361
+ Iteration: %i
362
+ Requester: %r
363
+ Owners: %o
364
+ State: %s
365
+ Labels: %l
366
+ URL: %u
367
+ Created: %c
368
+ Updated: %updated
369
+ Archived: %a
370
+ `;
371
+ const format = program2.format || defaultFormat;
372
+ const labels = story.labels.map((l) => `${l.name} (#${l.id})`);
373
+ const owners = story.owners.map(
374
+ (o) => `${o.profile.name} (${o.profile.mention_name})`
375
+ );
376
+ const url = `https://app.shortcut.com/story/${story.id}`;
377
+ const project = story.project ? `${story.project.name} (#${story.project.id})` : "None";
378
+ log(
379
+ format.replace(/%j/, JSON.stringify({ ...story, url }, null, 2)).replace(/%id/, import_chalk.default.blue.bold(`${story.id}`)).replace(/%t/, import_chalk.default.blue(`${story.name}`)).replace(/%d/, story.description || "").replace(/%y/, story.story_type).replace(/%l/, labels.join(", ") || "_").replace(
380
+ /%epic/,
381
+ story.epic_id ? `${(story.epic || {}).name} (#${story.epic_id})` : "_"
382
+ ).replace(/%e/, `${story.estimate || "_"}`).replace(
383
+ /%i/,
384
+ story.iteration_id ? `${(story.iteration || {}).name} (#${story.iteration_id})` : "_"
385
+ ).replace(/%p/, project).replace(/%T/, story.group?.name || "_").replace(/%o/, owners.join(", ") || "_").replace(
386
+ /%r/,
387
+ // eslint-disable-next-line no-constant-binary-expression
388
+ `${story.requester.profile.name} (${story.requester.profile.mention_name})` || "_"
389
+ ).replace(
390
+ /%s/,
391
+ `${(story.state || {}).name} (#${story.workflow_state_id})`
392
+ ).replace(/%c/, `${story.created_at}`).replace(
393
+ /%updated/,
394
+ `${story.updated_at !== story.created_at ? story.updated_at : "_"}`
395
+ ).replace(/%u/, url).replace(/%a/, `${story.archived}`).replace(
396
+ /%gbs/,
397
+ `${buildStoryBranch(story, `${config2.mentionName}/sc-${story.id}/`)}`
398
+ ).replace(/%gb/, `${buildStoryBranch(story)}`)
399
+ );
400
+ return story;
401
+ };
402
+ };
403
+ var buildURL = (...segments) => {
404
+ return [
405
+ "https://app.shortcut.com",
406
+ config2.urlSlug,
407
+ ...segments.map((item) => item.toString())
408
+ ].join("/");
409
+ };
410
+ var storyURL = (story) => buildURL("story", story.id);
411
+ var printDetailedStory = (story, entities = {}) => {
412
+ const labels = story.labels.map((l) => {
413
+ return import_chalk.default.bold(`#${l.id}`) + ` ${l.name}`;
414
+ });
415
+ const owners = story.owners.map((o) => {
416
+ const mentionName = import_chalk.default.bold(`${o.profile.mention_name}`);
417
+ return `${o.profile.name} (${mentionName})`;
418
+ });
419
+ log(import_chalk.default.blue.bold(`#${story.id}`) + import_chalk.default.blue(` ${story.name}`));
420
+ log(import_chalk.default.bold("Desc:") + ` ${formatLong(story.description || "_")}`);
421
+ log(import_chalk.default.bold("Team:") + ` ${story.group?.name || "_"}`);
422
+ log(import_chalk.default.bold("Owners:") + ` ${owners.join(", ") || "_"}`);
423
+ log(
424
+ import_chalk.default.bold("Requester:") + ` ${story.requester.profile.name} (${story.requester.profile.mention_name})`
425
+ );
426
+ log(import_chalk.default.bold("Type:") + ` ${story.story_type}/${story.estimate || "_"}`);
427
+ log(import_chalk.default.bold("Label:") + ` ${labels.join(", ") || "_"}`);
428
+ if (story.project) {
429
+ log(import_chalk.default.bold("Project:") + import_chalk.default.bold(` #${story.project_id} `) + story.project.name);
430
+ }
431
+ if (story.group) {
432
+ log(import_chalk.default.bold("Team:") + import_chalk.default.bold(` #${story.group_id} `) + story.group.name);
433
+ }
434
+ if (story.epic) {
435
+ log(import_chalk.default.bold("Epic:") + import_chalk.default.bold(` #${story.epic_id} `) + story.epic.name);
436
+ } else {
437
+ log(import_chalk.default.bold("Epic:") + " _");
438
+ }
439
+ if (story.iteration) {
440
+ log(
441
+ import_chalk.default.bold("Iteration:") + import_chalk.default.bold(` #${story.iteration_id} `) + story.iteration.name
442
+ );
443
+ } else {
444
+ log(import_chalk.default.bold("Iteration:") + " _");
445
+ }
446
+ log(import_chalk.default.bold("State:") + import_chalk.default.bold(` #${story.workflow_state_id} `) + story.state.name);
447
+ log(import_chalk.default.bold("Created:") + ` ${story.created_at}`);
448
+ if (story.created_at !== story.updated_at) {
449
+ log(import_chalk.default.bold("Updated:") + ` ${story.updated_at}`);
450
+ }
451
+ log(import_chalk.default.bold("URL:") + ` ${storyURL(story)}`);
452
+ if (story.archived) {
453
+ log(import_chalk.default.bold("Archived: ") + import_chalk.default.bold(`${story.archived}`));
454
+ }
455
+ if (story.completed) {
456
+ log(import_chalk.default.bold("Completed: ") + import_chalk.default.bold(`${story.completed_at}`));
457
+ }
458
+ story.tasks.map((c) => {
459
+ log(
460
+ import_chalk.default.bold("Task: ") + (c.complete ? "[X]" : "[ ]") + " " + formatLong(c.description)
461
+ );
462
+ return c;
463
+ });
464
+ story.comments.filter((comment) => !comment.deleted).map((c) => {
465
+ const author = entities.membersById.get(c.author_id);
466
+ log(import_chalk.default.bold("Comment:") + ` ${formatLong(c.text)}`);
467
+ log(` ${author.profile.name} ` + import_chalk.default.bold("at:") + ` ${c.updated_at}`);
468
+ return c;
469
+ });
470
+ story.files.map((file) => {
471
+ log(import_chalk.default.bold("File:") + ` ${file.name}`);
472
+ log(` ${file.url}`);
473
+ return file;
474
+ });
475
+ log();
476
+ };
477
+ var formatLong = (str) => str.split("\n").join("\n ");
478
+ var parseDateComparator = (arg) => {
479
+ const match = arg.match(/[0-9].*/) || { index: 0, "0": { length: 30 } };
480
+ const parsedDate = new Date(arg.slice(match.index));
481
+ const comparator = arg.slice(0, match.index);
482
+ return (date) => {
483
+ switch (comparator) {
484
+ case "<":
485
+ return new Date(date) < parsedDate;
486
+ case ">":
487
+ return new Date(date) > parsedDate;
488
+ case "=":
489
+ default:
490
+ return new Date(date.slice(0, match[0].length)).getTime() === parsedDate.getTime();
491
+ }
492
+ };
493
+ };
494
+ var parseNumberComparator = (arg) => {
495
+ const match = arg.match(/[0-9].*/) || { index: 0, "0": { length: 30 } };
496
+ const parsedNumber = Number(arg.slice(match.index));
497
+ const comparator = arg.slice(0, match.index).trimRight();
498
+ return (n) => {
499
+ switch (comparator) {
500
+ case "<":
501
+ return Number(n) < parsedNumber;
502
+ case ">":
503
+ return Number(n) > parsedNumber;
504
+ case "=":
505
+ default:
506
+ return Number(n) === parsedNumber;
507
+ }
508
+ };
509
+ };
510
+ var buildStoryBranch = (story, prefix = "") => {
511
+ prefix = prefix || `${config2.mentionName}/sc-${story.id}/${story.story_type}-`;
512
+ const slug = story.name.toLowerCase().replace(/\W/g, "-").replace(/[^a-z0-9-]/g, "").slice(0, 30).replace(/-$/, "");
513
+ return `${prefix}${slug}`;
514
+ };
515
+ var checkoutStoryBranch = (story, prefix = "") => {
516
+ const branch = buildStoryBranch(story, prefix);
517
+ debug("checking out git branch: " + branch);
518
+ (0, import_child_process.execSync)(`git checkout ${branch} 2> /dev/null || git checkout -b ${branch}`);
519
+ };
520
+ var fileURL = (file) => `${file.url}?token=${config2.token}`;
521
+ var stories_default = {
522
+ listStories,
523
+ printFormattedStory,
524
+ printDetailedStory,
525
+ checkoutStoryBranch,
526
+ fetchEntities,
527
+ hydrateStory,
528
+ findProject,
529
+ findGroup,
530
+ findState,
531
+ findEpic,
532
+ findIteration,
533
+ findOwnerIds,
534
+ findLabelNames,
535
+ fileURL,
536
+ storyURL,
537
+ buildURL
538
+ };
539
+
540
+ // src/bin/short-search.ts
541
+ var spin = spinner_default("Finding... %s ");
542
+ var log2 = console.log;
543
+ var program = import_commander.default.description(
544
+ `Search through Shortcut stories. Arguments (non-flag/options) will be
545
+ passed to Shortcut story search API as search operators. Passing '%self%' as
546
+ a search operator argument will be replaced by your mention name. Note that
547
+ passing search operators and options (e.g. --owner foobar) will use the
548
+ options as extra filtering in the client.
549
+
550
+ Refer to https://help.shortcut.com/hc/en-us/articles/360000046646-Search-Operators
551
+ for more details about search operators.`
552
+ ).usage("[options] [SEARCH OPERATORS]").option("-a, --archived", "Include archived Stories").option(
553
+ "-c, --created [operator][date]",
554
+ "Stories created within criteria (operator is one of <|>|=)",
555
+ ""
556
+ ).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(
557
+ "-e, --estimate [operator][number]",
558
+ "Stories estimated within criteria (operator is one of <|>|=)",
559
+ ""
560
+ ).option(
561
+ "-u, --updated [operator][date]",
562
+ "Stories updated within criteria (operator is one of <|>|=)",
563
+ ""
564
+ ).option("-y, --type [name]", "Stories of type, by regex", "").option(
565
+ "-r, --sort [field]",
566
+ "Sort stories by field (accessor[:asc|desc][,next])",
567
+ "state.position:asc,position:asc"
568
+ ).option("-f, --format [template]", "Format each story output by template", "");
569
+ var getWorkspaceOptions = (program2) => {
570
+ const blacklistedKeys = ["Command", "commands", "Option", "options", "rawArgs", "save"];
571
+ return Object.entries(program2).filter(([key]) => !(blacklistedKeys.includes(key) || key.startsWith("_"))).reduce((obj, [key, val]) => Object.assign(obj, { [key]: val }), {});
572
+ };
573
+ var main = async () => {
574
+ program.parse(process.argv);
575
+ if (!program.quiet) {
576
+ if (!program.args.length) {
577
+ log2("Fetching all stories for search since no search operators were passed ...");
578
+ }
579
+ spin.start();
580
+ }
581
+ let stories = [];
582
+ try {
583
+ stories = await stories_default.listStories(program);
584
+ } catch (e) {
585
+ log2("Error fetching stories:", e);
586
+ }
587
+ if (!program.quiet) spin.stop(true);
588
+ stories.map(stories_default.printFormattedStory(program));
589
+ if (!program.save) {
590
+ return;
591
+ }
592
+ const name = program.save === true ? "default" : program.save;
593
+ if (configure_default.saveWorkspace(name, getWorkspaceOptions(program))) {
594
+ log2("Saved query as %s workspace", name);
595
+ }
596
+ };
597
+ if (process.argv[1]?.includes("short-search")) {
598
+ main();
137
599
  }
138
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2hvcnQtc2VhcmNoLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2Jpbi9zaG9ydC1zZWFyY2gudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFDQSwwQ0FBcUM7QUFDckMscUNBQXVDO0FBRXZDLDhDQUF5QztBQUN6QywwQ0FBeUQ7QUFFekQsSUFBTSxJQUFJLEdBQUcsSUFBQSxpQkFBTyxFQUFDLGdCQUFnQixDQUFDLENBQUM7QUFDdkMsSUFBTSxHQUFHLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQztBQUVYLFFBQUEsT0FBTyxHQUFHLFNBQVM7S0FDM0IsV0FBVyxDQUNSLGtlQU9tQyxDQUN0QztLQUNBLEtBQUssQ0FBQyw4QkFBOEIsQ0FBQztLQUNyQyxNQUFNLENBQUMsZ0JBQWdCLEVBQUUsMEJBQTBCLENBQUM7S0FDcEQsTUFBTSxDQUNILGdDQUFnQyxFQUNoQyw0REFBNEQsRUFDNUQsRUFBRSxDQUNMO0tBQ0EsTUFBTSxDQUFDLGFBQWEsRUFBRSw0Q0FBNEMsRUFBRSxFQUFFLENBQUM7S0FDdkUsTUFBTSxDQUFDLHVCQUF1QixFQUFFLHNDQUFzQyxFQUFFLEVBQUUsQ0FBQztLQUMzRSxNQUFNLENBQUMsb0JBQW9CLEVBQUUsOEJBQThCLEVBQUUsRUFBRSxDQUFDO0tBQ2hFLE1BQU0sQ0FBQyxvQkFBb0IsRUFBRSxvQkFBb0IsRUFBRSxFQUFFLENBQUM7S0FDdEQsTUFBTSxDQUFDLHVCQUF1QixFQUFFLDZDQUE2QyxFQUFFLEVBQUUsQ0FBQztLQUNsRixNQUFNLENBQUMsa0JBQWtCLEVBQUUsbUNBQW1DLEVBQUUsRUFBRSxDQUFDO0tBQ25FLE1BQU0sQ0FBQywyQkFBMkIsRUFBRSx3Q0FBd0MsRUFBRSxFQUFFLENBQUM7S0FDakYsTUFBTSxDQUFDLG1CQUFtQixFQUFFLHdDQUF3QyxDQUFDO0tBQ3JFLE1BQU0sQ0FBQyxt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
600
+ // Annotate the CommonJS export names for ESM import in node:
601
+ 0 && (module.exports = {
602
+ main,
603
+ program
604
+ });