@shortcut-cli/shortcut-cli 3.8.1 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,47 +1,44 @@
1
- Object.defineProperty(exports, '__esModule', { value: true });
2
- const require_rolldown_runtime = require('../_virtual/rolldown_runtime.js');
3
- const require_lib_configure = require('./configure.js');
4
- const require_lib_client = require('./client.js');
5
- let debug = require("debug");
6
- debug = require_rolldown_runtime.__toESM(debug);
7
- let child_process = require("child_process");
8
- let chalk = require("chalk");
9
- chalk = require_rolldown_runtime.__toESM(chalk);
10
-
1
+ import { loadConfig } from "./configure.js";
2
+ import client from "./client.js";
3
+ import debugging from "debug";
4
+ import { execSync } from "child_process";
5
+ import chalk from "chalk";
11
6
  //#region src/lib/stories.ts
12
- const debug$1 = (0, debug.default)("club");
13
- const config = require_lib_configure.loadConfig();
7
+ const debug = debugging("club");
8
+ const config = loadConfig();
14
9
  const log = console.log;
15
10
  async function fetchEntities() {
16
- const [projectsById, statesById, membersById, groupsById, epicsById, iterationsById, labels] = await Promise.all([
17
- require_lib_client.default.listProjects().then((r) => r.data).then(mapByItemId),
18
- require_lib_client.default.listWorkflows().then((r) => r.data).then((wfs) => wfs.reduce((states, wf) => states.concat(wf.states), [])).then(mapByItemId),
19
- require_lib_client.default.listMembers(null).then((r) => r.data).then(mapByItemStringId),
20
- require_lib_client.default.listGroups().then((r) => r.data).then(mapByItemStringId),
21
- require_lib_client.default.listEpics(null).then((r) => r.data).then(mapByItemId),
22
- require_lib_client.default.listIterations(null).then((r) => r.data).then(mapByItemId),
23
- require_lib_client.default.listLabels(null).then((r) => r.data)
11
+ const [projectsById, statesById, membersById, groupsById, epicsById, objectivesById, iterationsById, labels] = await Promise.all([
12
+ client.listProjects().then((r) => r.data).then(mapByItemId),
13
+ client.listWorkflows().then((r) => r.data).then((wfs) => wfs.reduce((states, wf) => states.concat(wf.states), [])).then(mapByItemId),
14
+ client.listMembers().then((r) => r.data).then(mapByItemStringId),
15
+ client.listGroups().then((r) => r.data).then(mapByItemStringId),
16
+ client.listEpics().then((r) => r.data).then(mapByItemId),
17
+ client.listObjectives().then((r) => r.data).then(mapByItemId),
18
+ client.listIterations().then((r) => r.data).then(mapByItemId),
19
+ client.listLabels().then((r) => r.data)
24
20
  ]).catch((err) => {
25
21
  log(`Error fetching workflows: ${err}`);
26
22
  process.exit(2);
27
23
  });
28
- debug$1("response workflows, members, groups, projects, epics, iterations");
24
+ debug("response workflows, members, groups, projects, epics, iterations");
29
25
  return {
30
26
  projectsById,
31
27
  statesById,
32
28
  membersById,
33
29
  groupsById,
34
30
  epicsById,
31
+ objectivesById,
35
32
  iterationsById,
36
33
  labels
37
34
  };
38
35
  }
39
- const listStories = async (program) => {
40
- debug$1("request workflows, members, projects, epics");
36
+ const listStories = async (options) => {
37
+ debug("request workflows, members, projects, epics");
41
38
  const entities = await fetchEntities();
42
- const stories = await fetchStories(program, entities);
43
- debug$1("filtering stories");
44
- return filterStories(program, stories, entities).sort(sortStories(program));
39
+ const stories = await fetchStories(options, entities);
40
+ debug("filtering stories");
41
+ return filterStories(options, stories, entities).sort(sortStories(options));
45
42
  };
46
43
  function mapByItemId(items) {
47
44
  return items.reduce((map, obj) => map.set(obj.id, obj), /* @__PURE__ */ new Map());
@@ -49,24 +46,33 @@ function mapByItemId(items) {
49
46
  function mapByItemStringId(items) {
50
47
  return items.reduce((map, obj) => map.set(obj.id, obj), /* @__PURE__ */ new Map());
51
48
  }
52
- async function fetchStories(program, entities) {
53
- if ((program.args || []).length) {
54
- debug$1("using the search endpoint");
55
- return searchStories(program);
49
+ async function fetchStories(options, entities) {
50
+ if ((options.args ?? []).length) {
51
+ debug("using the search endpoint");
52
+ return searchStories(options);
56
53
  }
57
- debug$1("filtering projects");
58
- const regexProject = new RegExp(program.project, "i");
59
- const projectIds = [...entities.projectsById.values()].filter((p) => !!(p.id + p.name).match(regexProject));
60
- debug$1("request all stories for project(s)", projectIds.map((p) => p.name).join(", "));
61
- return Promise.all(projectIds.map((p) => require_lib_client.default.listStories(p.id, null))).then((projectStories) => projectStories.reduce((acc, stories) => acc.concat(stories.data), []));
54
+ const groups = entities.groupsById ? [...entities.groupsById.values()] : [];
55
+ if (groups.length > 0) {
56
+ debug("request all stories for group(s)", groups.map((g) => g.name).join(", "));
57
+ return Promise.all(groups.map((g) => client.listGroupStories(g.id))).then((groupStories) => {
58
+ return dedupeStoriesById(groupStories.reduce((acc, group) => acc.concat(group.data), []));
59
+ });
60
+ }
61
+ debug("filtering projects");
62
+ const regexProject = new RegExp(options.project ?? "", "i");
63
+ const projectIds = (entities.projectsById ? [...entities.projectsById.values()] : []).filter((p) => !!`${p.id} ${p.name}`.match(regexProject));
64
+ debug("request all stories for project(s)", projectIds.map((p) => p.name).join(", "));
65
+ return Promise.all(projectIds.map((p) => client.listStories(p.id))).then((projectStories) => {
66
+ return dedupeStoriesById(projectStories.reduce((acc, project) => acc.concat(project.data), []));
67
+ });
62
68
  }
63
- async function searchStories(program) {
64
- const query = program.args.join(" ").replace("%self%", config.mentionName);
65
- let result = await require_lib_client.default.searchStories({ query });
69
+ async function searchStories(options) {
70
+ const query = (options.args ?? []).join(" ").replace("%self%", config.mentionName ?? "");
71
+ let result = await client.searchStories({ query });
66
72
  let stories = result.data.data.map(storySearchResultToStory);
67
73
  while (result.data.next) {
68
- const nextCursor = new URLSearchParams(result.data.next).get("next");
69
- result = await require_lib_client.default.searchStories({
74
+ const nextCursor = new URLSearchParams(result.data.next).get("next") ?? void 0;
75
+ result = await client.searchStories({
70
76
  query,
71
77
  next: nextCursor
72
78
  });
@@ -88,97 +94,129 @@ const storySearchResultToStory = (storySearchResult) => {
88
94
  };
89
95
  };
90
96
  const hydrateStory = (entities, story) => {
91
- debug$1("hydrating story");
97
+ debug("hydrating story");
92
98
  const augmented = story;
93
- augmented.project = entities.projectsById.get(story.project_id);
94
- augmented.state = entities.statesById.get(story.workflow_state_id);
95
- augmented.epic = entities.epicsById.get(story.epic_id);
96
- augmented.iteration = entities.iterationsById.get(story.iteration_id);
97
- augmented.owners = story.owner_ids.map((id) => entities.membersById.get(id));
98
- augmented.requester = entities.membersById.get(story.requested_by_id);
99
- augmented.group = entities.groupsById.get(story.group_id);
100
- debug$1("hydrated story");
99
+ if (story.project_id !== null && story.project_id !== void 0) augmented.project = entities.projectsById?.get(story.project_id);
100
+ augmented.state = entities.statesById?.get(story.workflow_state_id);
101
+ if (story.epic_id !== null && story.epic_id !== void 0) augmented.epic = entities.epicsById?.get(story.epic_id);
102
+ if (story.iteration_id !== null && story.iteration_id !== void 0) augmented.iteration = entities.iterationsById?.get(story.iteration_id);
103
+ augmented.owners = story.owner_ids.map((id) => entities.membersById?.get(id));
104
+ if (story.requested_by_id) augmented.requester = entities.membersById?.get(story.requested_by_id);
105
+ if (story.group_id) augmented.group = entities.groupsById?.get(story.group_id);
106
+ debug("hydrated story");
101
107
  return augmented;
102
108
  };
103
109
  const isNumber = (val) => !!(val || val === 0) && !isNaN(Number(val.toString()));
104
110
  const findEntity = (entities, id) => {
111
+ if (!entities) return void 0;
105
112
  if (entities.get(id)) return entities.get(id);
106
113
  if (isNumber(id) && Number(id.toString())) return entities.get(Number(id.toString()));
107
114
  const match = new RegExp(`${id}`, "i");
108
115
  return Array.from(entities.values()).filter((s) => !!s.name.match(match))[0];
109
116
  };
110
117
  const findProject = (entities, project) => findEntity(entities.projectsById, project);
111
- const findGroup = (entities, group) => findEntity(entities.groupsById, group);
118
+ const findGroup = (entities, group) => entities.groupsById?.get(String(group)) || Array.from(entities.groupsById?.values() || []).find((g) => !!`${g.id} ${g.name} ${g.mention_name}`.match(new RegExp(`${group}`, "i")));
119
+ const findMember = (entities, member) => {
120
+ if (!entities.membersById) return void 0;
121
+ if (entities.membersById.get(String(member))) return entities.membersById.get(String(member));
122
+ const match = new RegExp(`${member}`, "i");
123
+ return Array.from(entities.membersById.values()).find((m) => !!`${m.id} ${m.profile.name} ${m.profile.mention_name}`.match(match));
124
+ };
125
+ const findLabel = (entities, label) => {
126
+ const labels = entities.labels ?? [];
127
+ const exact = labels.find((l) => `${l.id}` === `${label}`);
128
+ if (exact) return exact;
129
+ const match = new RegExp(`${label}`, "i");
130
+ return labels.find((l) => !!`${l.id} ${l.name}`.match(match));
131
+ };
112
132
  const findState = (entities, state) => findEntity(entities.statesById, state);
113
133
  const findEpic = (entities, epicName) => findEntity(entities.epicsById, epicName);
114
- const findIteration = (entities, iterationName) => findEntity(entities.statesById, iterationName);
134
+ const findObjective = (entities, objectiveName) => findEntity(entities.objectivesById, objectiveName);
135
+ const findIteration = (entities, iterationName) => findEntity(entities.iterationsById, iterationName);
136
+ const findObjectiveIds = (entities, objectives) => {
137
+ return objectives.split(",").map((objective) => objective.trim()).filter(Boolean).map((objective) => findObjective(entities, objective)?.id).filter((id) => typeof id === "number");
138
+ };
115
139
  const findOwnerIds = (entities, owners) => {
116
140
  const ownerMatch = new RegExp(owners.split(",").join("|"), "i");
117
- return Array.from(entities.membersById.values()).filter((m) => !!`${m.id} ${m.profile.name} ${m.profile.mention_name}`.match(ownerMatch)).map((m) => m.id);
141
+ return (entities.membersById ? Array.from(entities.membersById.values()) : []).filter((m) => !!`${m.id} ${m.profile.name} ${m.profile.mention_name}`.match(ownerMatch)).map((m) => m.id);
118
142
  };
119
143
  const findLabelNames = (entities, label) => {
120
144
  const labelMatch = new RegExp(label.split(",").join("|"), "i");
121
- return entities.labels.filter((m) => !!`${m.id} ${m.name}`.match(labelMatch)).map((m) => ({ name: m.name }));
145
+ return (entities.labels ?? []).filter((m) => !!`${m.id} ${m.name}`.match(labelMatch)).map((m) => ({ name: m.name }));
146
+ };
147
+ const dedupeStoriesById = (stories) => {
148
+ const seen = /* @__PURE__ */ new Set();
149
+ return stories.filter((story) => {
150
+ if (seen.has(story.id)) return false;
151
+ seen.add(story.id);
152
+ return true;
153
+ });
122
154
  };
123
- const filterStories = (program, stories, entities) => {
124
- let created_at;
125
- if (program.created) created_at = parseDateComparator(program.created);
126
- let updated_at;
127
- if (program.updated) updated_at = parseDateComparator(program.updated);
128
- let estimate;
129
- if (program.estimate) estimate = parseNumberComparator(program.estimate);
130
- const regexLabel = new RegExp(program.label, "i");
131
- const regexState = new RegExp(program.state, "i");
132
- const regexOwner = new RegExp(program.owner, "i");
133
- const regexText = new RegExp(program.text, "i");
134
- const regexType = new RegExp(program.type, "i");
135
- const regexEpic = new RegExp(program.epic, "i");
136
- const regexIteration = new RegExp(program.iteration, "i");
155
+ const filterStories = (options, stories, entities) => {
156
+ let createdAtFilter;
157
+ if (options.created) createdAtFilter = parseDateComparator(options.created);
158
+ let updatedAtFilter;
159
+ if (options.updated) updatedAtFilter = parseDateComparator(options.updated);
160
+ let estimateFilter;
161
+ if (options.estimate) estimateFilter = parseNumberComparator(options.estimate);
162
+ const regexLabel = new RegExp(options.label ?? "", "i");
163
+ const regexState = new RegExp(options.state ?? "", "i");
164
+ const regexOwner = new RegExp(options.owner ?? "", "i");
165
+ const regexText = new RegExp(options.text ?? "", "i");
166
+ const regexType = new RegExp(options.type ?? "", "i");
167
+ const regexEpic = new RegExp(options.epic ?? "", "i");
168
+ const regexIteration = new RegExp(options.iteration ?? "", "i");
169
+ const regexProject = new RegExp(options.project ?? "", "i");
137
170
  return stories.map((story) => hydrateStory(entities, story)).filter((s) => {
138
- if (!program.archived && s.archived) return false;
171
+ if (!options.archived && s.archived) return false;
139
172
  if (!(s.labels.map((l) => `${l.id},${l.name}`).join(",") + "").match(regexLabel)) return false;
140
- if (!(s.workflow_state_id + " " + (s.state || {}).name).match(regexState)) return false;
141
- if (!(s.epic_id + " " + (s.epic || {}).name).match(regexEpic)) return false;
142
- if (!(s.iteration_id + " " + (s.iteration || {}).name).match(regexIteration)) return false;
143
- if (program.owner) {
144
- if (!(s.owners.filter((o) => {
145
- return !!`${o.profile.name} ${o.profile.mention_name}`.match(regexOwner);
173
+ if (!(s.workflow_state_id + " " + (s.state?.name ?? "")).match(regexState)) return false;
174
+ if (!(s.epic_id + " " + (s.epic?.name ?? "")).match(regexEpic)) return false;
175
+ if (!(s.iteration_id + " " + (s.iteration?.name ?? "")).match(regexIteration)) return false;
176
+ if (!(s.project_id + " " + (s.project?.name ?? "")).match(regexProject)) return false;
177
+ if (options.owner) {
178
+ if (!((s.owners ?? []).filter((o) => {
179
+ return !!`${o?.profile.name} ${o?.profile.mention_name}`.match(regexOwner);
146
180
  }).length > 0)) return false;
147
181
  }
148
182
  if (!s.name.match(regexText)) return false;
149
183
  if (!s.story_type.match(regexType)) return false;
150
- if (created_at && !created_at(s.created_at)) return false;
151
- if (updated_at && !updated_at(s.updated_at)) return false;
152
- return !(estimate && !estimate(s.estimate));
184
+ if (createdAtFilter && !createdAtFilter(s.created_at)) return false;
185
+ if (updatedAtFilter && !updatedAtFilter(s.updated_at)) return false;
186
+ return !(estimateFilter && !estimateFilter(s.estimate));
153
187
  });
154
188
  };
155
- const sortStories = (program) => {
156
- const fields = (program.sort || "").split(",").map((s) => {
157
- return s.split(":").map((ss) => ss.split("."));
189
+ const sortStories = (options) => {
190
+ const fields = (options.sort ?? "").split(",").map((s) => {
191
+ const parts = s.split(":");
192
+ return [(parts[0] ?? "").split("."), parts[1]?.split(".")];
158
193
  });
159
194
  const pluck = (acc, val) => {
160
- if (acc[val] === void 0) return {};
161
- return acc[val];
195
+ if (!acc || typeof acc !== "object") return;
196
+ const value = acc[val];
197
+ if (value === void 0 || value === null) return void 0;
198
+ return value;
162
199
  };
163
- debug$1("sorting stories");
200
+ debug("sorting stories");
164
201
  return (a, b) => {
165
202
  return fields.reduce((acc, field) => {
166
203
  if (acc !== 0) return acc;
167
204
  const ap = field[0].reduce(pluck, a);
168
205
  const bp = field[0].reduce(pluck, b);
169
206
  if (ap === bp) return 0;
170
- const direction = (field[1] || [""])[0].match(/des/i) ? 1 : -1;
171
- if (ap > bp) {
207
+ const direction = (field[1]?.[0] ?? "").match(/des/i) ? 1 : -1;
208
+ if (ap === void 0 || ap === null) return direction > 0 ? 1 : -1;
209
+ if (bp === void 0 || bp === null) return direction > 0 ? -1 : 1;
210
+ if (String(ap) > String(bp)) {
172
211
  if (direction > 0) return -1;
173
212
  } else if (direction < 0) return -1;
174
213
  return 1;
175
214
  }, 0);
176
215
  };
177
216
  };
178
- const printFormattedStory = (program) => {
217
+ const printFormattedStory = (options) => {
179
218
  return (story) => {
180
- var _story$group;
181
- const format = program.format || `#%id %t
219
+ const format = options.format || `#%id %t
182
220
  \tType: %y/%e
183
221
  \tTeam: %T
184
222
  \tProject: %p
@@ -194,13 +232,14 @@ const printFormattedStory = (program) => {
194
232
  \tArchived: %a
195
233
  `;
196
234
  const labels = story.labels.map((l) => `${l.name} (#${l.id})`);
197
- const owners = story.owners.map((o) => `${o.profile.name} (${o.profile.mention_name})`);
235
+ const owners = story.owners?.map((o) => o ? `${o.profile.name} (${o.profile.mention_name})` : "Unknown") ?? [];
198
236
  const url = storyURL(story);
199
237
  const project = story.project ? `${story.project.name} (#${story.project.id})` : "None";
238
+ const requesterStr = story.requester ? `${story.requester.profile.name} (${story.requester.profile.mention_name})` : "_";
200
239
  log(format.replace(/%j/, JSON.stringify({
201
240
  ...story,
202
241
  url
203
- }, null, 2)).replace(/%id/, chalk.default.blue.bold(`${story.id}`)).replace(/%t/, chalk.default.blue(`${story.name}`)).replace(/%d/, story.description || "").replace(/%y/, story.story_type).replace(/%l/, labels.join(", ") || "_").replace(/%epic/, story.epic_id ? `${(story.epic || {}).name} (#${story.epic_id})` : "_").replace(/%e/, `${story.estimate || "_"}`).replace(/%i/, story.iteration_id ? `${(story.iteration || {}).name} (#${story.iteration_id})` : "_").replace(/%p/, project).replace(/%T/, ((_story$group = story.group) === null || _story$group === void 0 ? void 0 : _story$group.name) || "_").replace(/%o/, owners.join(", ") || "_").replace(/%r/, `${story.requester.profile.name} (${story.requester.profile.mention_name})` || "_").replace(/%s/, `${(story.state || {}).name} (#${story.workflow_state_id})`).replace(/%c/, `${story.created_at}`).replace(/%updated/, `${story.updated_at !== story.created_at ? story.updated_at : "_"}`).replace(/%u/, url).replace(/%a/, `${story.archived}`).replace(/%gbs/, `${buildStoryBranch(story, `${config.mentionName}/sc-${story.id}/`)}`).replace(/%gb/, `${buildStoryBranch(story)}`));
242
+ }, null, 2)).replace(/%id/, chalk.blue.bold(`${story.id}`)).replace(/%t/, chalk.blue(`${story.name}`)).replace(/%d/, story.description || "").replace(/%y/, story.story_type).replace(/%l/, labels.join(", ") || "_").replace(/%epic/, story.epic_id ? `${story.epic?.name ?? ""} (#${story.epic_id})` : "_").replace(/%e/, `${story.estimate || "_"}`).replace(/%i/, story.iteration_id ? `${story.iteration?.name ?? ""} (#${story.iteration_id})` : "_").replace(/%p/, project).replace(/%T/, story.group?.name || "_").replace(/%o/, owners.join(", ") || "_").replace(/%r/, requesterStr).replace(/%s/, `${story.state?.name ?? ""} (#${story.workflow_state_id})`).replace(/%c/, `${story.created_at}`).replace(/%updated/, `${story.updated_at !== story.created_at ? story.updated_at : "_"}`).replace(/%u/, url).replace(/%a/, `${story.archived}`).replace(/%gbs/, `${buildStoryBranch(story, `${config.mentionName}/sc-${story.id}/`)}`).replace(/%gb/, `${buildStoryBranch(story)}`));
204
243
  return story;
205
244
  };
206
245
  };
@@ -213,51 +252,52 @@ const buildURL = (...segments) => {
213
252
  };
214
253
  const storyURL = (story) => buildURL("story", story.id);
215
254
  const printDetailedStory = (story, entities = {}) => {
216
- var _story$group2;
217
255
  const labels = story.labels.map((l) => {
218
- return chalk.default.bold(`#${l.id}`) + ` ${l.name}`;
256
+ return chalk.bold(`#${l.id}`) + ` ${l.name}`;
219
257
  });
220
- const owners = story.owners.map((o) => {
221
- const mentionName = chalk.default.bold(`${o.profile.mention_name}`);
258
+ const owners = story.owners?.map((o) => {
259
+ if (!o) return "Unknown";
260
+ const mentionName = chalk.bold(`${o.profile.mention_name}`);
222
261
  return `${o.profile.name} (${mentionName})`;
223
- });
224
- log(chalk.default.blue.bold(`#${story.id}`) + chalk.default.blue(` ${story.name}`));
225
- log(chalk.default.bold("Desc:") + ` ${formatLong(story.description || "_")}`);
226
- log(chalk.default.bold("Team:") + ` ${((_story$group2 = story.group) === null || _story$group2 === void 0 ? void 0 : _story$group2.name) || "_"}`);
227
- log(chalk.default.bold("Owners:") + ` ${owners.join(", ") || "_"}`);
228
- log(chalk.default.bold("Requester:") + ` ${story.requester.profile.name} (${story.requester.profile.mention_name})`);
229
- log(chalk.default.bold("Type:") + ` ${story.story_type}/${story.estimate || "_"}`);
230
- log(chalk.default.bold("Label:") + ` ${labels.join(", ") || "_"}`);
231
- if (story.project) log(chalk.default.bold("Project:") + chalk.default.bold(` #${story.project_id} `) + story.project.name);
232
- if (story.group) log(chalk.default.bold("Team:") + chalk.default.bold(` #${story.group_id} `) + story.group.name);
233
- if (story.epic) log(chalk.default.bold("Epic:") + chalk.default.bold(` #${story.epic_id} `) + story.epic.name);
234
- else log(chalk.default.bold("Epic:") + " _");
235
- if (story.iteration) log(chalk.default.bold("Iteration:") + chalk.default.bold(` #${story.iteration_id} `) + story.iteration.name);
236
- else log(chalk.default.bold("Iteration:") + " _");
237
- log(chalk.default.bold("State:") + chalk.default.bold(` #${story.workflow_state_id} `) + story.state.name);
238
- log(chalk.default.bold("Created:") + ` ${story.created_at}`);
239
- if (story.created_at !== story.updated_at) log(chalk.default.bold("Updated:") + ` ${story.updated_at}`);
240
- log(chalk.default.bold("URL:") + ` ${storyURL(story)}`);
241
- if (story.archived) log(chalk.default.bold("Archived: ") + chalk.default.bold(`${story.archived}`));
242
- if (story.completed) log(chalk.default.bold("Completed: ") + chalk.default.bold(`${story.completed_at}`));
243
- story.tasks.map((c) => {
244
- log(chalk.default.bold("Task: ") + (c.complete ? "[X]" : "[ ]") + " " + formatLong(c.description));
262
+ }) ?? [];
263
+ log(chalk.blue.bold(`#${story.id}`) + chalk.blue(` ${story.name}`));
264
+ log(chalk.bold("Desc:") + ` ${formatLong(story.description || "_")}`);
265
+ log(chalk.bold("Team:") + ` ${story.group?.name || "_"}`);
266
+ log(chalk.bold("Owners:") + ` ${owners.join(", ") || "_"}`);
267
+ const requesterStr = story.requester ? `${story.requester.profile.name} (${story.requester.profile.mention_name})` : "_";
268
+ log(chalk.bold("Requester:") + ` ${requesterStr}`);
269
+ log(chalk.bold("Type:") + ` ${story.story_type}/${story.estimate || "_"}`);
270
+ log(chalk.bold("Label:") + ` ${labels.join(", ") || "_"}`);
271
+ if (story.project) log(chalk.bold("Project:") + chalk.bold(` #${story.project_id} `) + story.project.name);
272
+ if (story.group) log(chalk.bold("Team:") + chalk.bold(` #${story.group_id} `) + story.group.name);
273
+ if (story.epic) log(chalk.bold("Epic:") + chalk.bold(` #${story.epic_id} `) + story.epic.name);
274
+ else log(chalk.bold("Epic:") + " _");
275
+ if (story.iteration) log(chalk.bold("Iteration:") + chalk.bold(` #${story.iteration_id} `) + story.iteration.name);
276
+ else log(chalk.bold("Iteration:") + " _");
277
+ log(chalk.bold("State:") + chalk.bold(` #${story.workflow_state_id} `) + (story.state?.name ?? ""));
278
+ log(chalk.bold("Created:") + ` ${story.created_at}`);
279
+ if (story.created_at !== story.updated_at) log(chalk.bold("Updated:") + ` ${story.updated_at}`);
280
+ log(chalk.bold("URL:") + ` ${storyURL(story)}`);
281
+ if (story.archived) log(chalk.bold("Archived: ") + chalk.bold(`${story.archived}`));
282
+ if (story.completed) log(chalk.bold("Completed: ") + chalk.bold(`${story.completed_at}`));
283
+ if ("tasks" in story) story.tasks.map((c) => {
284
+ log(chalk.bold("Task: ") + (c.complete ? "[X]" : "[ ]") + " " + formatLong(c.description));
245
285
  return c;
246
286
  });
247
- story.comments.filter((comment) => !comment.deleted).map((c) => {
248
- const author = entities.membersById.get(c.author_id);
249
- log(chalk.default.bold("Comment:") + ` ${formatLong(c.text)}`);
250
- log(` ${author.profile.name} ` + chalk.default.bold("at:") + ` ${c.updated_at}`);
287
+ if ("comments" in story) story.comments.filter((comment) => !comment.deleted).map((c) => {
288
+ const author = c.author_id ? entities.membersById?.get(c.author_id) : void 0;
289
+ log(chalk.bold("Comment:") + ` ${formatLong(c.text)}`);
290
+ log(` ${author?.profile.name ?? "Unknown"} ` + chalk.bold("at:") + ` ${c.updated_at}`);
251
291
  return c;
252
292
  });
253
- story.files.map((file) => {
254
- log(chalk.default.bold("File:") + ` ${file.name}`);
293
+ if ("files" in story) story.files.map((file) => {
294
+ log(chalk.bold("File:") + ` ${file.name}`);
255
295
  log(` ${file.url}`);
256
296
  return file;
257
297
  });
258
298
  log();
259
299
  };
260
- const formatLong = (str) => str.split("\n").join("\n ");
300
+ const formatLong = (str) => (str ?? "").split("\n").join("\n ");
261
301
  const parseDateComparator = (arg) => {
262
302
  const match = arg.match(/[0-9].*/) || {
263
303
  index: 0,
@@ -266,10 +306,10 @@ const parseDateComparator = (arg) => {
266
306
  const parsedDate = new Date(arg.slice(match.index));
267
307
  const comparator = arg.slice(0, match.index);
268
308
  return (date) => {
309
+ if (!date) return false;
269
310
  switch (comparator) {
270
311
  case "<": return new Date(date) < parsedDate;
271
312
  case ">": return new Date(date) > parsedDate;
272
- case "=":
273
313
  default: return new Date(date.slice(0, match[0].length)).getTime() === parsedDate.getTime();
274
314
  }
275
315
  };
@@ -285,7 +325,6 @@ const parseNumberComparator = (arg) => {
285
325
  switch (comparator) {
286
326
  case "<": return Number(n) < parsedNumber;
287
327
  case ">": return Number(n) > parsedNumber;
288
- case "=":
289
328
  default: return Number(n) === parsedNumber;
290
329
  }
291
330
  };
@@ -297,8 +336,8 @@ const buildStoryBranch = (story, prefix = "") => {
297
336
  };
298
337
  const checkoutStoryBranch = (story, prefix = "") => {
299
338
  const branch = buildStoryBranch(story, prefix);
300
- debug$1("checking out git branch: " + branch);
301
- (0, child_process.execSync)(`git checkout ${branch} 2> /dev/null || git checkout -b ${branch}`);
339
+ debug("checking out git branch: " + branch);
340
+ execSync(`git checkout ${branch} 2> /dev/null || git checkout -b ${branch}`);
302
341
  };
303
342
  const fileURL = (file) => `${file.url}?token=${config.token}`;
304
343
  var stories_default = {
@@ -310,8 +349,12 @@ var stories_default = {
310
349
  hydrateStory,
311
350
  findProject,
312
351
  findGroup,
352
+ findMember,
353
+ findLabel,
313
354
  findState,
314
355
  findEpic,
356
+ findObjective,
357
+ findObjectiveIds,
315
358
  findIteration,
316
359
  findOwnerIds,
317
360
  findLabelNames,
@@ -319,6 +362,5 @@ var stories_default = {
319
362
  storyURL,
320
363
  buildURL
321
364
  };
322
-
323
365
  //#endregion
324
- exports.default = stories_default;
366
+ export { stories_default as default };
package/build/package.js CHANGED
@@ -1,18 +1,5 @@
1
-
2
1
  //#region package.json
3
- var version = "3.8.1";
4
- var description = "A community-driven command line tool for viewing, creating, and updating shortcut.com stories";
5
-
2
+ var version = "5.0.0";
3
+ var description = "A community-driven command line tool for viewing, creating, and updating shortcut.com";
6
4
  //#endregion
7
- Object.defineProperty(exports, 'description', {
8
- enumerable: true,
9
- get: function () {
10
- return description;
11
- }
12
- });
13
- Object.defineProperty(exports, 'version', {
14
- enumerable: true,
15
- get: function () {
16
- return version;
17
- }
18
- });
5
+ export { description, version };
package/package.json CHANGED
@@ -1,69 +1,69 @@
1
1
  {
2
- "name": "@shortcut-cli/shortcut-cli",
3
- "version": "3.8.1",
4
- "description": "A community-driven command line tool for viewing, creating, and updating shortcut.com stories",
5
- "engines": {
6
- "node": ">=16"
7
- },
8
- "files": [
9
- "build"
10
- ],
11
- "scripts": {
12
- "build": "tsdown",
13
- "build:watch": "npm run build -- --watch",
14
- "prepublishOnly": "npm run clean && npm run build",
15
- "clean": "rm -rf build",
16
- "start": "node -r source-map-support/register build/bin/short.js",
17
- "test": "npm run test:format",
18
- "test:format": "prettier --list-different src/**/*.ts",
19
- "format": "prettier --write src/**/*.ts",
20
- "ci": "npm run build && npm run test",
21
- "lint": "eslint .",
22
- "type-check": "tsc --noEmit"
23
- },
24
- "bin": {
25
- "short": "./build/bin/short.js"
26
- },
27
- "repository": {
28
- "type": "git",
29
- "url": "git+https://github.com/shortcut-cli/shortcut-cli.git"
30
- },
31
- "keywords": [
32
- "clubhouse",
33
- "cli",
34
- "shortcut"
35
- ],
36
- "author": "email@andjosh.com",
37
- "license": "MIT",
38
- "bugs": {
39
- "url": "https://github.com/shortcut-cli/shortcut-cli/issues"
40
- },
41
- "homepage": "https://github.com/shortcut-cli/shortcut-cli",
42
- "dependencies": {
43
- "@shortcut/client": "^3.1.0",
44
- "chalk": "^2.2.0",
45
- "cli-spinner": "^0.2.10",
46
- "commander": "^2.12.0",
47
- "debug": "^4.4.1",
48
- "prompt": "^1.3.0"
49
- },
50
- "devDependencies": {
51
- "@eslint/js": "^9.39.1",
52
- "@total-typescript/tsconfig": "^1.0.4",
53
- "@types/chalk": "2.2.4",
54
- "@types/cli-spinner": "0.2.3",
55
- "@types/debug": "4.1.12",
56
- "@types/node": "24.10.1",
57
- "@types/node-fetch": "3.0.3",
58
- "@types/prompt": "^1.1.9",
59
- "eslint": "^9.39.1",
60
- "eslint-plugin-import": "^2.32.0",
61
- "glob": "^11.0.3",
62
- "globals": "^16.5.0",
63
- "prettier": "3.6.2",
64
- "source-map-support": "0.5.21",
65
- "tsdown": "^0.12.9",
66
- "typescript": "5.9.3",
67
- "typescript-eslint": "^8.47.0"
68
- }
2
+ "name": "@shortcut-cli/shortcut-cli",
3
+ "version": "5.0.0",
4
+ "description": "A community-driven command line tool for viewing, creating, and updating shortcut.com",
5
+ "keywords": [
6
+ "shortcut",
7
+ "cli"
8
+ ],
9
+ "homepage": "https://github.com/shortcut-cli/shortcut-cli",
10
+ "bugs": {
11
+ "url": "https://github.com/shortcut-cli/shortcut-cli/issues"
12
+ },
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/shortcut-cli/shortcut-cli.git"
16
+ },
17
+ "license": "MIT",
18
+ "author": "email@andjosh.com",
19
+ "packageManager": "pnpm@10.32.0",
20
+ "type": "module",
21
+ "engines": {
22
+ "node": ">=20.19.0"
23
+ },
24
+ "bin": {
25
+ "short": "./build/bin/short.js"
26
+ },
27
+ "files": [
28
+ "build"
29
+ ],
30
+ "scripts": {
31
+ "build": "tsdown",
32
+ "build:watch": "pnpm run build --watch",
33
+ "ci": "pnpm run build && pnpm run test && pnpm run test:format",
34
+ "clean": "rm -rf build",
35
+ "format": "oxfmt",
36
+ "lint": "oxlint .",
37
+ "prepublishOnly": "pnpm run clean && pnpm run build",
38
+ "start": "node -r source-map-support/register build/bin/short.js",
39
+ "test": "vitest run --coverage",
40
+ "test:format": "oxfmt --check",
41
+ "test:update-spec": "curl -sL -o test/fixtures/shortcut.swagger.json https://developer.shortcut.com/api/rest/v3/shortcut.swagger.json && node test/scripts/patch-spec.mjs",
42
+ "test:watch": "vitest",
43
+ "type-check": "tsc --noEmit"
44
+ },
45
+ "dependencies": {
46
+ "@shortcut/client": "^3.2.0",
47
+ "chalk": "^5.6.2",
48
+ "cli-spinner": "^0.2.10",
49
+ "commander": "^14.0.3",
50
+ "debug": "^4.4.3"
51
+ },
52
+ "devDependencies": {
53
+ "@stoplight/http-spec": "7.1.0",
54
+ "@stoplight/prism-http": "5.12.0",
55
+ "@stoplight/prism-http-server": "5.12.2",
56
+ "@total-typescript/tsconfig": "^1.0.4",
57
+ "@types/cli-spinner": "0.2.3",
58
+ "@types/debug": "4.1.12",
59
+ "@types/node": "24.10.1",
60
+ "@vitest/coverage-v8": "4.0.18",
61
+ "oxfmt": "0.37.0",
62
+ "oxlint": "1.52.0",
63
+ "pino": "10.3.1",
64
+ "source-map-support": "0.5.21",
65
+ "tsdown": "0.21.1",
66
+ "typescript": "5.9.3",
67
+ "vitest": "4.0.18"
68
+ }
69
69
  }