@shortcut-cli/shortcut-cli 3.8.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/README.md +121 -2
  2. package/build/bin/{short-api.js → short-api.cjs} +12 -10
  3. package/build/bin/short-create.cjs +58 -0
  4. package/build/bin/{short-doc.js → short-doc.cjs} +8 -9
  5. package/build/bin/{short-docs.js → short-docs.cjs} +18 -19
  6. package/build/bin/{short-epic.js → short-epic.cjs} +8 -9
  7. package/build/bin/short-epics.cjs +36 -0
  8. package/build/bin/{short-find.js → short-find.cjs} +1 -1
  9. package/build/bin/{short-install.js → short-install.cjs} +8 -9
  10. package/build/bin/short-iteration.cjs +184 -0
  11. package/build/bin/short-iterations.cjs +66 -0
  12. package/build/bin/{short-members.js → short-members.cjs} +6 -7
  13. package/build/bin/{short-projects.js → short-projects.cjs} +7 -8
  14. package/build/bin/{short-search.js → short-search.cjs} +20 -25
  15. package/build/bin/{short-story.js → short-story.cjs} +47 -46
  16. package/build/bin/{short-workflows.js → short-workflows.cjs} +5 -6
  17. package/build/bin/short-workspace.cjs +64 -0
  18. package/build/bin/short.cjs +10 -0
  19. package/build/lib/client.cjs +11 -0
  20. package/build/lib/{configure.js → configure.cjs} +3 -3
  21. package/build/lib/{spinner.js → spinner.cjs} +1 -1
  22. package/build/lib/{stories.js → stories.cjs} +73 -69
  23. package/build/{package.js → package.cjs} +1 -1
  24. package/package.json +67 -67
  25. package/build/bin/short-create.js +0 -56
  26. package/build/bin/short-epics.js +0 -37
  27. package/build/bin/short-workspace.js +0 -64
  28. package/build/bin/short.js +0 -11
  29. package/build/lib/client.js +0 -11
  30. /package/build/_virtual/{rolldown_runtime.js → rolldown_runtime.cjs} +0 -0
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env node
2
+ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
3
+ const require_lib_configure = require('../lib/configure.cjs');
4
+ const require_lib_stories = require('../lib/stories.cjs');
5
+ const require_bin_short_search = require('./short-search.cjs');
6
+ let commander = require("commander");
7
+
8
+ //#region src/bin/short-workspace.ts
9
+ const config = require_lib_configure.default.loadConfig();
10
+ const log = console.log;
11
+ const program = new commander.Command().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);
12
+ const opts = program.opts();
13
+ const toArgs = (obj) => Object.entries(obj).map(([k, v]) => `--${k} '${v}'`).join(" ");
14
+ const main = async () => {
15
+ if (!config || !config.token) {
16
+ log("Not installed yet.");
17
+ log("Please run: short install");
18
+ return;
19
+ } else if (!config.workspaces) {
20
+ log("No workspace saved.");
21
+ log("Please run:");
22
+ log(" short search [options] --save");
23
+ log("to create your first one.");
24
+ return;
25
+ } else if (opts.list) {
26
+ log("Workspaces:");
27
+ Object.keys(config.workspaces).map((w) => {
28
+ log(" ", w + ":", toArgs(config.workspaces[w]));
29
+ });
30
+ return;
31
+ } else if (opts.unset) {
32
+ if (require_lib_configure.default.removeWorkspace(opts.unset)) log("Successfully removed %s workspace", opts.unset);
33
+ else log("Failed to remove %s workspace", opts.unset);
34
+ return;
35
+ }
36
+ const name = `${opts.name || program.args[0] || "default"}`;
37
+ const workspace = config.workspaces[name];
38
+ if (!workspace) {
39
+ log("No workspace saved with name", name);
40
+ log("Please run:");
41
+ log(" short search [options] --save", name);
42
+ log("to create it.");
43
+ return;
44
+ }
45
+ const foundOpts = require_bin_short_search.program.parse(process.argv).opts();
46
+ const additionalArgs = {
47
+ ...workspace,
48
+ ...Object.fromEntries(Object.entries(foundOpts).filter(([, v]) => v !== void 0))
49
+ };
50
+ if (!opts.quiet) {
51
+ log("Loading %s workspace ...", name);
52
+ log();
53
+ }
54
+ let stories = [];
55
+ try {
56
+ stories = await require_lib_stories.default.listStories(additionalArgs);
57
+ } catch (e) {
58
+ log("Error fetching stories:", e);
59
+ }
60
+ stories.map(require_lib_stories.default.printFormattedStory(additionalArgs));
61
+ };
62
+ main();
63
+
64
+ //#endregion
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
3
+ const require_package = require('../package.cjs');
4
+ let commander = require("commander");
5
+
6
+ //#region src/bin/short.ts
7
+ process.on("unhandledRejection", console.log);
8
+ new commander.Command().version(require_package.version).description(require_package.description).command("install [options]", "install and configure API access").command("search [options] [SEARCH OPERATORS]", "search stories with optional query").alias("s").command("find [options] [SEARCH OPERATORS]", "[DEPRECATED] search stories with optional query").command("story ID [options]", "view or manipulate stories").alias("st").command("create [options]", "create a story").alias("c").command("members [options]", "list members").alias("m").command("workflows [options]", "list workflows and their states").alias("wf").command("epics [options]", "list epics and their states").alias("e").command("epic [command] [options]", "create or view an epic").command("iterations [options]", "list iterations").alias("i").command("iteration [command] [options]", "view, create, update, or delete an iteration").command("docs [options]", "list and search docs").alias("d").command("doc [command] [options]", "view, create, or update a doc").command("projects [options]", "list projects and their states").alias("p").command("workspace [NAME] [options]", "list stories matching saved workspace query", { isDefault: true }).alias("w").command("api <path> [options]", "make a request to the Shortcut API").parse(process.argv);
9
+
10
+ //#endregion
@@ -0,0 +1,11 @@
1
+ Object.defineProperty(exports, '__esModule', { value: true });
2
+ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
3
+ const require_lib_configure = require('./configure.cjs');
4
+ let _shortcut_client = require("@shortcut/client");
5
+
6
+ //#region src/lib/client.ts
7
+ const client = new _shortcut_client.ShortcutClient(require_lib_configure.loadConfig().token);
8
+ var client_default = client;
9
+
10
+ //#endregion
11
+ exports.default = client_default;
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, '__esModule', { value: true });
2
- const require_rolldown_runtime = require('../_virtual/rolldown_runtime.js');
2
+ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
3
3
  let path = require("path");
4
4
  path = require_rolldown_runtime.__toESM(path);
5
5
  let fs = require("fs");
@@ -21,7 +21,7 @@ let CONFIG_CACHE = null;
21
21
  */
22
22
  const loadConfig = () => {
23
23
  const config = loadCachedConfig();
24
- if (!config || config === {} || !config.token) {
24
+ if (!config || !config.token) {
25
25
  console.error("Please run 'short install' to configure Shortcut API access or set SHORTCUT_API_TOKEN.");
26
26
  process.exit(11);
27
27
  }
@@ -93,7 +93,7 @@ const updateConfig = (newConfig) => {
93
93
  };
94
94
  const saveWorkspace = (name, workspace) => {
95
95
  const extantConfig = loadCachedConfig();
96
- const workspaces = extantConfig.workspaces || {};
96
+ const workspaces = extantConfig.workspaces ?? {};
97
97
  workspaces[name] = workspace;
98
98
  return saveConfig({
99
99
  workspaces,
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, '__esModule', { value: true });
2
- const require_rolldown_runtime = require('../_virtual/rolldown_runtime.js');
2
+ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
3
3
  let cli_spinner = require("cli-spinner");
4
4
 
5
5
  //#region src/lib/spinner.ts
@@ -1,7 +1,7 @@
1
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');
2
+ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
3
+ const require_lib_configure = require('./configure.cjs');
4
+ const require_lib_client = require('./client.cjs');
5
5
  let debug = require("debug");
6
6
  debug = require_rolldown_runtime.__toESM(debug);
7
7
  let child_process = require("child_process");
@@ -36,12 +36,12 @@ async function fetchEntities() {
36
36
  labels
37
37
  };
38
38
  }
39
- const listStories = async (program) => {
39
+ const listStories = async (options) => {
40
40
  debug$1("request workflows, members, projects, epics");
41
41
  const entities = await fetchEntities();
42
- const stories = await fetchStories(program, entities);
42
+ const stories = await fetchStories(options, entities);
43
43
  debug$1("filtering stories");
44
- return filterStories(program, stories, entities).sort(sortStories(program));
44
+ return filterStories(options, stories, entities).sort(sortStories(options));
45
45
  };
46
46
  function mapByItemId(items) {
47
47
  return items.reduce((map, obj) => map.set(obj.id, obj), /* @__PURE__ */ new Map());
@@ -49,19 +49,19 @@ function mapByItemId(items) {
49
49
  function mapByItemStringId(items) {
50
50
  return items.reduce((map, obj) => map.set(obj.id, obj), /* @__PURE__ */ new Map());
51
51
  }
52
- async function fetchStories(program, entities) {
53
- if ((program.args || []).length) {
52
+ async function fetchStories(options, entities) {
53
+ if ((options.args ?? []).length) {
54
54
  debug$1("using the search endpoint");
55
- return searchStories(program);
55
+ return searchStories(options);
56
56
  }
57
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));
58
+ const regexProject = new RegExp(options.project ?? "", "i");
59
+ const projectIds = (entities.projectsById ? [...entities.projectsById.values()] : []).filter((p) => !!(p.id + p.name).match(regexProject));
60
60
  debug$1("request all stories for project(s)", projectIds.map((p) => p.name).join(", "));
61
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), []));
62
62
  }
63
- async function searchStories(program) {
64
- const query = program.args.join(" ").replace("%self%", config.mentionName);
63
+ async function searchStories(options) {
64
+ const query = (options.args ?? []).join(" ").replace("%self%", config.mentionName ?? "");
65
65
  let result = await require_lib_client.default.searchStories({ query });
66
66
  let stories = result.data.data.map(storySearchResultToStory);
67
67
  while (result.data.next) {
@@ -90,18 +90,19 @@ const storySearchResultToStory = (storySearchResult) => {
90
90
  const hydrateStory = (entities, story) => {
91
91
  debug$1("hydrating story");
92
92
  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);
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
100
  debug$1("hydrated story");
101
101
  return augmented;
102
102
  };
103
103
  const isNumber = (val) => !!(val || val === 0) && !isNaN(Number(val.toString()));
104
104
  const findEntity = (entities, id) => {
105
+ if (!entities) return void 0;
105
106
  if (entities.get(id)) return entities.get(id);
106
107
  if (isNumber(id) && Number(id.toString())) return entities.get(Number(id.toString()));
107
108
  const match = new RegExp(`${id}`, "i");
@@ -111,54 +112,56 @@ const findProject = (entities, project) => findEntity(entities.projectsById, pro
111
112
  const findGroup = (entities, group) => findEntity(entities.groupsById, group);
112
113
  const findState = (entities, state) => findEntity(entities.statesById, state);
113
114
  const findEpic = (entities, epicName) => findEntity(entities.epicsById, epicName);
114
- const findIteration = (entities, iterationName) => findEntity(entities.statesById, iterationName);
115
+ const findIteration = (entities, iterationName) => findEntity(entities.iterationsById, iterationName);
115
116
  const findOwnerIds = (entities, owners) => {
116
117
  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);
118
+ 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
119
  };
119
120
  const findLabelNames = (entities, label) => {
120
121
  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 }));
122
+ return (entities.labels ?? []).filter((m) => !!`${m.id} ${m.name}`.match(labelMatch)).map((m) => ({ name: m.name }));
122
123
  };
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");
124
+ const filterStories = (options, stories, entities) => {
125
+ let createdAtFilter;
126
+ if (options.created) createdAtFilter = parseDateComparator(options.created);
127
+ let updatedAtFilter;
128
+ if (options.updated) updatedAtFilter = parseDateComparator(options.updated);
129
+ let estimateFilter;
130
+ if (options.estimate) estimateFilter = parseNumberComparator(options.estimate);
131
+ const regexLabel = new RegExp(options.label ?? "", "i");
132
+ const regexState = new RegExp(options.state ?? "", "i");
133
+ const regexOwner = new RegExp(options.owner ?? "", "i");
134
+ const regexText = new RegExp(options.text ?? "", "i");
135
+ const regexType = new RegExp(options.type ?? "", "i");
136
+ const regexEpic = new RegExp(options.epic ?? "", "i");
137
+ const regexIteration = new RegExp(options.iteration ?? "", "i");
137
138
  return stories.map((story) => hydrateStory(entities, story)).filter((s) => {
138
- if (!program.archived && s.archived) return false;
139
+ if (!options.archived && s.archived) return false;
139
140
  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);
141
+ if (!(s.workflow_state_id + " " + (s.state?.name ?? "")).match(regexState)) return false;
142
+ if (!(s.epic_id + " " + (s.epic?.name ?? "")).match(regexEpic)) return false;
143
+ if (!(s.iteration_id + " " + (s.iteration?.name ?? "")).match(regexIteration)) return false;
144
+ if (options.owner) {
145
+ if (!(s.owners?.filter((o) => {
146
+ return !!`${o?.profile.name} ${o?.profile.mention_name}`.match(regexOwner);
146
147
  }).length > 0)) return false;
147
148
  }
148
149
  if (!s.name.match(regexText)) return false;
149
150
  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));
151
+ if (createdAtFilter && !createdAtFilter(s.created_at)) return false;
152
+ if (updatedAtFilter && !updatedAtFilter(s.updated_at)) return false;
153
+ return !(estimateFilter && !estimateFilter(s.estimate));
153
154
  });
154
155
  };
155
- const sortStories = (program) => {
156
- const fields = (program.sort || "").split(",").map((s) => {
157
- return s.split(":").map((ss) => ss.split("."));
156
+ const sortStories = (options) => {
157
+ const fields = (options.sort ?? "").split(",").map((s) => {
158
+ const parts = s.split(":");
159
+ return [parts[0].split("."), parts[1]?.split(".")];
158
160
  });
159
161
  const pluck = (acc, val) => {
160
- if (acc[val] === void 0) return {};
161
- return acc[val];
162
+ const value = acc[val];
163
+ if (value === void 0) return {};
164
+ return value;
162
165
  };
163
166
  debug$1("sorting stories");
164
167
  return (a, b) => {
@@ -167,7 +170,7 @@ const sortStories = (program) => {
167
170
  const ap = field[0].reduce(pluck, a);
168
171
  const bp = field[0].reduce(pluck, b);
169
172
  if (ap === bp) return 0;
170
- const direction = (field[1] || [""])[0].match(/des/i) ? 1 : -1;
173
+ const direction = (field[1]?.[0] ?? "").match(/des/i) ? 1 : -1;
171
174
  if (ap > bp) {
172
175
  if (direction > 0) return -1;
173
176
  } else if (direction < 0) return -1;
@@ -175,10 +178,9 @@ const sortStories = (program) => {
175
178
  }, 0);
176
179
  };
177
180
  };
178
- const printFormattedStory = (program) => {
181
+ const printFormattedStory = (options) => {
179
182
  return (story) => {
180
- var _story$group;
181
- const format = program.format || `#%id %t
183
+ const format = options.format || `#%id %t
182
184
  \tType: %y/%e
183
185
  \tTeam: %T
184
186
  \tProject: %p
@@ -194,13 +196,14 @@ const printFormattedStory = (program) => {
194
196
  \tArchived: %a
195
197
  `;
196
198
  const labels = story.labels.map((l) => `${l.name} (#${l.id})`);
197
- const owners = story.owners.map((o) => `${o.profile.name} (${o.profile.mention_name})`);
199
+ const owners = story.owners?.map((o) => o ? `${o.profile.name} (${o.profile.mention_name})` : "Unknown") ?? [];
198
200
  const url = storyURL(story);
199
201
  const project = story.project ? `${story.project.name} (#${story.project.id})` : "None";
202
+ const requesterStr = story.requester ? `${story.requester.profile.name} (${story.requester.profile.mention_name})` : "_";
200
203
  log(format.replace(/%j/, JSON.stringify({
201
204
  ...story,
202
205
  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)}`));
206
+ }, 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?.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
207
  return story;
205
208
  };
206
209
  };
@@ -213,19 +216,20 @@ const buildURL = (...segments) => {
213
216
  };
214
217
  const storyURL = (story) => buildURL("story", story.id);
215
218
  const printDetailedStory = (story, entities = {}) => {
216
- var _story$group2;
217
219
  const labels = story.labels.map((l) => {
218
220
  return chalk.default.bold(`#${l.id}`) + ` ${l.name}`;
219
221
  });
220
- const owners = story.owners.map((o) => {
222
+ const owners = story.owners?.map((o) => {
223
+ if (!o) return "Unknown";
221
224
  const mentionName = chalk.default.bold(`${o.profile.mention_name}`);
222
225
  return `${o.profile.name} (${mentionName})`;
223
- });
226
+ }) ?? [];
224
227
  log(chalk.default.blue.bold(`#${story.id}`) + chalk.default.blue(` ${story.name}`));
225
228
  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) || "_"}`);
229
+ log(chalk.default.bold("Team:") + ` ${story.group?.name || "_"}`);
227
230
  log(chalk.default.bold("Owners:") + ` ${owners.join(", ") || "_"}`);
228
- log(chalk.default.bold("Requester:") + ` ${story.requester.profile.name} (${story.requester.profile.mention_name})`);
231
+ const requesterStr = story.requester ? `${story.requester.profile.name} (${story.requester.profile.mention_name})` : "_";
232
+ log(chalk.default.bold("Requester:") + ` ${requesterStr}`);
229
233
  log(chalk.default.bold("Type:") + ` ${story.story_type}/${story.estimate || "_"}`);
230
234
  log(chalk.default.bold("Label:") + ` ${labels.join(", ") || "_"}`);
231
235
  if (story.project) log(chalk.default.bold("Project:") + chalk.default.bold(` #${story.project_id} `) + story.project.name);
@@ -234,23 +238,23 @@ const printDetailedStory = (story, entities = {}) => {
234
238
  else log(chalk.default.bold("Epic:") + " _");
235
239
  if (story.iteration) log(chalk.default.bold("Iteration:") + chalk.default.bold(` #${story.iteration_id} `) + story.iteration.name);
236
240
  else log(chalk.default.bold("Iteration:") + " _");
237
- log(chalk.default.bold("State:") + chalk.default.bold(` #${story.workflow_state_id} `) + story.state.name);
241
+ log(chalk.default.bold("State:") + chalk.default.bold(` #${story.workflow_state_id} `) + (story.state?.name ?? ""));
238
242
  log(chalk.default.bold("Created:") + ` ${story.created_at}`);
239
243
  if (story.created_at !== story.updated_at) log(chalk.default.bold("Updated:") + ` ${story.updated_at}`);
240
244
  log(chalk.default.bold("URL:") + ` ${storyURL(story)}`);
241
245
  if (story.archived) log(chalk.default.bold("Archived: ") + chalk.default.bold(`${story.archived}`));
242
246
  if (story.completed) log(chalk.default.bold("Completed: ") + chalk.default.bold(`${story.completed_at}`));
243
- story.tasks.map((c) => {
247
+ if ("tasks" in story) story.tasks.map((c) => {
244
248
  log(chalk.default.bold("Task: ") + (c.complete ? "[X]" : "[ ]") + " " + formatLong(c.description));
245
249
  return c;
246
250
  });
247
- story.comments.filter((comment) => !comment.deleted).map((c) => {
248
- const author = entities.membersById.get(c.author_id);
251
+ if ("comments" in story) story.comments.filter((comment) => !comment.deleted).map((c) => {
252
+ const author = entities.membersById?.get(c.author_id);
249
253
  log(chalk.default.bold("Comment:") + ` ${formatLong(c.text)}`);
250
- log(` ${author.profile.name} ` + chalk.default.bold("at:") + ` ${c.updated_at}`);
254
+ log(` ${author?.profile.name ?? "Unknown"} ` + chalk.default.bold("at:") + ` ${c.updated_at}`);
251
255
  return c;
252
256
  });
253
- story.files.map((file) => {
257
+ if ("files" in story) story.files.map((file) => {
254
258
  log(chalk.default.bold("File:") + ` ${file.name}`);
255
259
  log(` ${file.url}`);
256
260
  return file;
@@ -1,6 +1,6 @@
1
1
 
2
2
  //#region package.json
3
- var version = "3.8.0";
3
+ var version = "4.0.0";
4
4
  var description = "A community-driven command line tool for viewing, creating, and updating shortcut.com stories";
5
5
 
6
6
  //#endregion
package/package.json CHANGED
@@ -1,69 +1,69 @@
1
1
  {
2
- "name": "@shortcut-cli/shortcut-cli",
3
- "version": "3.8.0",
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": "4.0.0",
4
+ "description": "A community-driven command line tool for viewing, creating, and updating shortcut.com stories",
5
+ "engines": {
6
+ "node": ">=18"
7
+ },
8
+ "packageManager": "pnpm@10.28.0",
9
+ "files": [
10
+ "build"
11
+ ],
12
+ "scripts": {
13
+ "build": "tsdown",
14
+ "build:watch": "pnpm run build --watch",
15
+ "prepublishOnly": "pnpm run clean && pnpm run build",
16
+ "clean": "rm -rf build",
17
+ "start": "node -r source-map-support/register build/bin/short.cjs",
18
+ "test": "pnpm run test:format",
19
+ "test:format": "prettier --list-different src/**/*.ts",
20
+ "format": "prettier --write src/**/*.ts",
21
+ "ci": "pnpm run build && pnpm run test",
22
+ "lint": "eslint .",
23
+ "type-check": "tsc --noEmit"
24
+ },
25
+ "bin": {
26
+ "short": "./build/bin/short.cjs"
27
+ },
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "git+https://github.com/shortcut-cli/shortcut-cli.git"
31
+ },
32
+ "keywords": [
33
+ "clubhouse",
34
+ "cli",
35
+ "shortcut"
36
+ ],
37
+ "author": "email@andjosh.com",
38
+ "license": "MIT",
39
+ "bugs": {
40
+ "url": "https://github.com/shortcut-cli/shortcut-cli/issues"
41
+ },
42
+ "homepage": "https://github.com/shortcut-cli/shortcut-cli",
43
+ "dependencies": {
44
+ "@shortcut/client": "^3.1.0",
45
+ "chalk": "^2.2.0",
46
+ "cli-spinner": "^0.2.10",
47
+ "commander": "^12.1.0",
48
+ "debug": "^4.4.3",
49
+ "prompt": "^1.3.0"
50
+ },
51
+ "devDependencies": {
52
+ "@eslint/js": "^9.39.2",
53
+ "@total-typescript/tsconfig": "^1.0.4",
54
+ "@types/chalk": "2.2.4",
55
+ "@types/cli-spinner": "0.2.3",
56
+ "@types/debug": "4.1.12",
57
+ "@types/node": "24.10.1",
58
+ "@types/node-fetch": "3.0.3",
59
+ "@types/prompt": "^1.1.9",
60
+ "eslint": "^9.39.2",
61
+ "eslint-plugin-import": "^2.32.0",
62
+ "globals": "^16.5.0",
63
+ "prettier": "3.7.4",
64
+ "source-map-support": "0.5.21",
65
+ "tsdown": "0.19.0",
66
+ "typescript": "5.9.3",
67
+ "typescript-eslint": "^8.53.0"
68
+ }
69
69
  }
@@ -1,56 +0,0 @@
1
- #!/usr/bin/env node
2
- const require_rolldown_runtime = require('../_virtual/rolldown_runtime.js');
3
- const require_lib_spinner = require('../lib/spinner.js');
4
- const require_lib_configure = require('../lib/configure.js');
5
- const require_lib_client = require('../lib/client.js');
6
- const require_lib_stories = require('../lib/stories.js');
7
- let commander = require("commander");
8
- commander = require_rolldown_runtime.__toESM(commander);
9
- let child_process = require("child_process");
10
-
11
- //#region src/bin/short-create.ts
12
- const config = require_lib_configure.loadConfig();
13
- const spin = require_lib_spinner.default();
14
- const log = console.log;
15
- const program = commander.default.usage("[options]").description("create a story with provided details").option("-d, --description [text]", "Set description of story", "").option("-e, --estimate [number]", "Set estimate of story").option("--epic [id|name]", "Set epic of story").option("--git-branch", "Checkout git branch from story slug <mention-name>/ch<id>/<type>-<title>\n as required by the Git integration: https://bit.ly/2RKO1FF").option("--git-branch-short", "Checkout git branch from story slug <mention-name>/ch<id>/<title>").option("-i, --iteration [id|name]", "Set iteration of story").option("-I, --idonly", "Print only ID of story result").option("-l, --label [id|name]", "Stories with label id/name, by regex", "").option("-o, --owners [id|name]", "Set owners of story, comma-separated", "").option("-O, --open", "Open story in browser").option("-p, --project [id|name]", "Set project of story, required if --state is not set", "").option("-T, --team [id|name]", "Set team of story", "").option("-t, --title [text]", "Set title of story, required", "").option("-s, --state [id|name]", "Set workflow state of story, required if --project is not set", "").option("-y, --type [name]", "Set type of story, default: feature", "feature").parse(process.argv);
16
- const main = async () => {
17
- const entities = await require_lib_stories.default.fetchEntities();
18
- if (!program.idonly) spin.start();
19
- const update = {
20
- name: program.title,
21
- story_type: program.type,
22
- description: `${program.description}`,
23
- estimate: program.estimate || void 0
24
- };
25
- if (program.project) update.project_id = (require_lib_stories.default.findProject(entities, program.project) || {}).id;
26
- if (program.team) update.group_id = (require_lib_stories.default.findGroup(entities, program.team) || {}).id;
27
- if (program.state) update.workflow_state_id = (require_lib_stories.default.findState(entities, program.state) || {}).id;
28
- if (program.epic) update.epic_id = (require_lib_stories.default.findEpic(entities, program.epic) || {}).id;
29
- if (program.iteration) update.iteration_id = (require_lib_stories.default.findIteration(entities, program.iteration) || {}).id;
30
- if (program.estimate) update.estimate = parseInt(program.estimate, 10);
31
- if (program.owners) update.owner_ids = require_lib_stories.default.findOwnerIds(entities, program.owners);
32
- if (program.label) update.labels = require_lib_stories.default.findLabelNames(entities, program.label);
33
- let story;
34
- if (!update.name) {
35
- if (!program.idonly) spin.stop(true);
36
- log("Must provide --title");
37
- } else if (!update.project_id && !update.workflow_state_id) {
38
- if (!program.idonly) spin.stop(true);
39
- log("Must provide --project or --state");
40
- } else try {
41
- story = await require_lib_client.default.createStory(update).then((r) => r.data);
42
- } catch (e) {
43
- log("Error creating story");
44
- }
45
- if (!program.idonly) spin.stop(true);
46
- if (story) {
47
- const hydrateStory = require_lib_stories.default.hydrateStory(entities, story);
48
- require_lib_stories.default.printDetailedStory(hydrateStory);
49
- if (program.gitBranch) require_lib_stories.default.checkoutStoryBranch(hydrateStory);
50
- else if (program.gitBranchShort) require_lib_stories.default.checkoutStoryBranch(hydrateStory, `${config.mentionName}/sc-${story.id}/`);
51
- if (program.open) (0, child_process.exec)("open " + require_lib_stories.default.storyURL(story));
52
- }
53
- };
54
- main();
55
-
56
- //#endregion