@shortcut-cli/shortcut-cli 3.8.1 → 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.
- package/README.md +121 -2
- package/build/bin/{short-api.js → short-api.cjs} +12 -10
- package/build/bin/short-create.cjs +58 -0
- package/build/bin/{short-doc.js → short-doc.cjs} +8 -9
- package/build/bin/{short-docs.js → short-docs.cjs} +18 -19
- package/build/bin/{short-epic.js → short-epic.cjs} +8 -9
- package/build/bin/short-epics.cjs +36 -0
- package/build/bin/{short-find.js → short-find.cjs} +1 -1
- package/build/bin/{short-install.js → short-install.cjs} +8 -9
- package/build/bin/short-iteration.cjs +184 -0
- package/build/bin/short-iterations.cjs +66 -0
- package/build/bin/{short-members.js → short-members.cjs} +6 -7
- package/build/bin/{short-projects.js → short-projects.cjs} +7 -8
- package/build/bin/{short-search.js → short-search.cjs} +20 -25
- package/build/bin/{short-story.js → short-story.cjs} +47 -46
- package/build/bin/{short-workflows.js → short-workflows.cjs} +5 -6
- package/build/bin/short-workspace.cjs +64 -0
- package/build/bin/short.cjs +10 -0
- package/build/lib/client.cjs +11 -0
- package/build/lib/{configure.js → configure.cjs} +3 -3
- package/build/lib/{spinner.js → spinner.cjs} +1 -1
- package/build/lib/{stories.js → stories.cjs} +73 -69
- package/build/{package.js → package.cjs} +1 -1
- package/package.json +67 -67
- package/build/bin/short-create.js +0 -56
- package/build/bin/short-epics.js +0 -37
- package/build/bin/short-workspace.js +0 -64
- package/build/bin/short.js +0 -11
- package/build/lib/client.js +0 -11
- /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.
|
|
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 ||
|
|
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.
|
|
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.
|
|
3
|
-
const require_lib_configure = require('./configure.
|
|
4
|
-
const require_lib_client = require('./client.
|
|
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 (
|
|
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(
|
|
42
|
+
const stories = await fetchStories(options, entities);
|
|
43
43
|
debug$1("filtering stories");
|
|
44
|
-
return filterStories(
|
|
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(
|
|
53
|
-
if ((
|
|
52
|
+
async function fetchStories(options, entities) {
|
|
53
|
+
if ((options.args ?? []).length) {
|
|
54
54
|
debug$1("using the search endpoint");
|
|
55
|
-
return searchStories(
|
|
55
|
+
return searchStories(options);
|
|
56
56
|
}
|
|
57
57
|
debug$1("filtering projects");
|
|
58
|
-
const regexProject = new RegExp(
|
|
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(
|
|
64
|
-
const query =
|
|
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
|
|
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
|
|
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.
|
|
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 = (
|
|
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(
|
|
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 (!
|
|
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
|
|
141
|
-
if (!(s.epic_id + " " + (s.epic
|
|
142
|
-
if (!(s.iteration_id + " " + (s.iteration
|
|
143
|
-
if (
|
|
144
|
-
if (!(s.owners
|
|
145
|
-
return !!`${o
|
|
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 (
|
|
151
|
-
if (
|
|
152
|
-
return !(
|
|
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 = (
|
|
156
|
-
const fields = (
|
|
157
|
-
|
|
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
|
-
|
|
161
|
-
return
|
|
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]
|
|
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 = (
|
|
181
|
+
const printFormattedStory = (options) => {
|
|
179
182
|
return (story) => {
|
|
180
|
-
|
|
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
|
|
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 ? `${
|
|
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
|
|
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:") + ` ${
|
|
229
|
+
log(chalk.default.bold("Team:") + ` ${story.group?.name || "_"}`);
|
|
227
230
|
log(chalk.default.bold("Owners:") + ` ${owners.join(", ") || "_"}`);
|
|
228
|
-
|
|
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
|
|
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
|
|
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
|
|
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;
|
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": "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
|