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