@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.
- package/README.md +549 -10
- package/build/bin/short-api.js +21 -22
- package/build/bin/short-create.js +58 -38
- package/build/bin/short-custom-field.js +50 -0
- package/build/bin/short-custom-fields.js +29 -0
- package/build/bin/short-doc.js +38 -41
- package/build/bin/short-docs.js +34 -27
- package/build/bin/short-epic.js +147 -36
- package/build/bin/short-epics.js +23 -24
- package/build/bin/short-find.js +4 -5
- package/build/bin/short-install.js +73 -29
- package/build/bin/short-iteration.js +180 -0
- package/build/bin/short-iterations.js +62 -0
- package/build/bin/short-label.js +130 -0
- package/build/bin/short-labels.js +27 -0
- package/build/bin/short-members.js +17 -21
- package/build/bin/short-objective.js +151 -0
- package/build/bin/short-objectives.js +63 -0
- package/build/bin/short-projects.js +17 -21
- package/build/bin/short-search.js +23 -32
- package/build/bin/short-story.js +350 -104
- package/build/bin/short-team.js +78 -0
- package/build/bin/short-teams.js +28 -0
- package/build/bin/short-workflows.js +12 -16
- package/build/bin/short-workspace.js +27 -28
- package/build/bin/short.js +5 -8
- package/build/lib/client.js +7 -9
- package/build/lib/configure.js +20 -29
- package/build/lib/spinner.js +3 -8
- package/build/lib/stories.js +171 -129
- package/build/package.js +3 -16
- package/package.json +67 -67
- package/build/_virtual/rolldown_runtime.js +0 -29
package/build/lib/stories.js
CHANGED
|
@@ -1,47 +1,44 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
|
13
|
-
const config =
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
|
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 (
|
|
40
|
-
debug
|
|
36
|
+
const listStories = async (options) => {
|
|
37
|
+
debug("request workflows, members, projects, epics");
|
|
41
38
|
const entities = await fetchEntities();
|
|
42
|
-
const stories = await fetchStories(
|
|
43
|
-
debug
|
|
44
|
-
return filterStories(
|
|
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(
|
|
53
|
-
if ((
|
|
54
|
-
debug
|
|
55
|
-
return searchStories(
|
|
49
|
+
async function fetchStories(options, entities) {
|
|
50
|
+
if ((options.args ?? []).length) {
|
|
51
|
+
debug("using the search endpoint");
|
|
52
|
+
return searchStories(options);
|
|
56
53
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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(
|
|
64
|
-
const query =
|
|
65
|
-
let result = await
|
|
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
|
|
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
|
|
97
|
+
debug("hydrating story");
|
|
92
98
|
const augmented = story;
|
|
93
|
-
augmented.project = entities.projectsById
|
|
94
|
-
augmented.state = entities.statesById
|
|
95
|
-
augmented.epic = entities.epicsById
|
|
96
|
-
augmented.iteration = entities.iterationsById
|
|
97
|
-
augmented.owners = story.owner_ids.map((id) => entities.membersById
|
|
98
|
-
augmented.requester = entities.membersById
|
|
99
|
-
augmented.group = entities.groupsById
|
|
100
|
-
debug
|
|
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) =>
|
|
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
|
|
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 = (
|
|
124
|
-
let
|
|
125
|
-
if (
|
|
126
|
-
let
|
|
127
|
-
if (
|
|
128
|
-
let
|
|
129
|
-
if (
|
|
130
|
-
const regexLabel = new RegExp(
|
|
131
|
-
const regexState = new RegExp(
|
|
132
|
-
const regexOwner = new RegExp(
|
|
133
|
-
const regexText = new RegExp(
|
|
134
|
-
const regexType = new RegExp(
|
|
135
|
-
const regexEpic = new RegExp(
|
|
136
|
-
const regexIteration = new RegExp(
|
|
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 (!
|
|
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
|
|
141
|
-
if (!(s.epic_id + " " + (s.epic
|
|
142
|
-
if (!(s.iteration_id + " " + (s.iteration
|
|
143
|
-
if (
|
|
144
|
-
|
|
145
|
-
|
|
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 (
|
|
151
|
-
if (
|
|
152
|
-
return !(
|
|
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 = (
|
|
156
|
-
const fields = (
|
|
157
|
-
|
|
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
|
|
161
|
-
|
|
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
|
|
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]
|
|
171
|
-
if (ap
|
|
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 = (
|
|
217
|
+
const printFormattedStory = (options) => {
|
|
179
218
|
return (story) => {
|
|
180
|
-
|
|
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
|
|
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.
|
|
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.
|
|
256
|
+
return chalk.bold(`#${l.id}`) + ` ${l.name}`;
|
|
219
257
|
});
|
|
220
|
-
const owners = story.owners
|
|
221
|
-
|
|
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.
|
|
225
|
-
log(chalk.
|
|
226
|
-
log(chalk.
|
|
227
|
-
log(chalk.
|
|
228
|
-
|
|
229
|
-
log(chalk.
|
|
230
|
-
log(chalk.
|
|
231
|
-
|
|
232
|
-
if (story.
|
|
233
|
-
if (story.
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
log(chalk.
|
|
238
|
-
log(chalk.
|
|
239
|
-
|
|
240
|
-
log(chalk.
|
|
241
|
-
|
|
242
|
-
if (story.
|
|
243
|
-
story.
|
|
244
|
-
|
|
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
|
|
249
|
-
log(chalk.
|
|
250
|
-
log(` ${author
|
|
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.
|
|
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
|
|
301
|
-
|
|
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
|
-
|
|
366
|
+
export { stories_default as default };
|
package/build/package.js
CHANGED
|
@@ -1,18 +1,5 @@
|
|
|
1
|
-
|
|
2
1
|
//#region package.json
|
|
3
|
-
var version = "
|
|
4
|
-
var description = "A community-driven command line tool for viewing, creating, and updating shortcut.com
|
|
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
|
-
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
"
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
"
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
}
|