@shortcut-cli/shortcut-cli 4.0.0 → 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 +428 -8
- package/build/bin/{short-api.cjs → short-api.js} +13 -16
- package/build/bin/short-create.js +76 -0
- package/build/bin/short-custom-field.js +50 -0
- package/build/bin/short-custom-fields.js +29 -0
- package/build/bin/{short-doc.cjs → short-doc.js} +34 -36
- package/build/bin/{short-docs.cjs → short-docs.js} +23 -15
- package/build/bin/short-epic.js +186 -0
- package/build/bin/short-epics.js +36 -0
- package/build/bin/short-find.js +6 -0
- package/build/bin/short-install.js +87 -0
- package/build/bin/{short-iteration.cjs → short-iteration.js} +41 -45
- package/build/bin/{short-iterations.cjs → short-iterations.js} +15 -19
- package/build/bin/short-label.js +130 -0
- package/build/bin/short-labels.js +27 -0
- package/build/bin/short-members.js +31 -0
- package/build/bin/short-objective.js +151 -0
- package/build/bin/short-objectives.js +63 -0
- package/build/bin/short-projects.js +31 -0
- package/build/bin/short-search.js +45 -0
- package/build/bin/short-story.js +458 -0
- package/build/bin/short-team.js +78 -0
- package/build/bin/short-teams.js +28 -0
- package/build/bin/short-workflows.js +29 -0
- package/build/bin/short-workspace.js +63 -0
- package/build/bin/short.js +8 -0
- package/build/lib/client.js +9 -0
- package/build/lib/{configure.cjs → configure.js} +18 -27
- package/build/lib/spinner.js +12 -0
- package/build/lib/{stories.cjs → stories.js} +116 -78
- package/build/package.js +5 -0
- package/package.json +44 -44
- package/build/_virtual/rolldown_runtime.cjs +0 -29
- package/build/bin/short-create.cjs +0 -58
- package/build/bin/short-epic.cjs +0 -74
- package/build/bin/short-epics.cjs +0 -36
- package/build/bin/short-find.cjs +0 -7
- package/build/bin/short-install.cjs +0 -42
- package/build/bin/short-members.cjs +0 -34
- package/build/bin/short-projects.cjs +0 -34
- package/build/bin/short-search.cjs +0 -49
- package/build/bin/short-story.cjs +0 -213
- package/build/bin/short-workflows.cjs +0 -32
- package/build/bin/short-workspace.cjs +0 -64
- package/build/bin/short.cjs +0 -10
- package/build/lib/client.cjs +0 -11
- package/build/lib/spinner.cjs +0 -17
- package/build/package.cjs +0 -18
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
|
|
3
|
-
const require_lib_spinner = require('../lib/spinner.cjs');
|
|
4
|
-
const require_lib_configure = require('../lib/configure.cjs');
|
|
5
|
-
const require_lib_client = require('../lib/client.cjs');
|
|
6
|
-
const require_lib_stories = require('../lib/stories.cjs');
|
|
7
|
-
let commander = require("commander");
|
|
8
|
-
let child_process = require("child_process");
|
|
9
|
-
|
|
10
|
-
//#region src/bin/short-create.ts
|
|
11
|
-
const config = require_lib_configure.loadConfig();
|
|
12
|
-
const spin = require_lib_spinner.default();
|
|
13
|
-
const log = console.log;
|
|
14
|
-
const opts = new commander.Command().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", "").addOption(new commander.Option("-y, --type <name>", "Set type of story").choices([
|
|
15
|
-
"feature",
|
|
16
|
-
"bug",
|
|
17
|
-
"chore"
|
|
18
|
-
]).default("feature")).parse(process.argv).opts();
|
|
19
|
-
const main = async () => {
|
|
20
|
-
const entities = await require_lib_stories.default.fetchEntities();
|
|
21
|
-
if (!opts.idonly) spin.start();
|
|
22
|
-
const update = {
|
|
23
|
-
name: opts.title,
|
|
24
|
-
story_type: opts.type,
|
|
25
|
-
description: `${opts.description}`
|
|
26
|
-
};
|
|
27
|
-
if (opts.project) update.project_id = require_lib_stories.default.findProject(entities, opts.project)?.id;
|
|
28
|
-
if (opts.team) update.group_id = require_lib_stories.default.findGroup(entities, opts.team)?.id;
|
|
29
|
-
if (opts.state) update.workflow_state_id = require_lib_stories.default.findState(entities, opts.state)?.id;
|
|
30
|
-
if (opts.epic) update.epic_id = require_lib_stories.default.findEpic(entities, opts.epic)?.id;
|
|
31
|
-
if (opts.iteration) update.iteration_id = require_lib_stories.default.findIteration(entities, opts.iteration)?.id;
|
|
32
|
-
if (opts.estimate) update.estimate = parseInt(opts.estimate, 10);
|
|
33
|
-
if (opts.owners) update.owner_ids = require_lib_stories.default.findOwnerIds(entities, opts.owners);
|
|
34
|
-
if (opts.label) update.labels = require_lib_stories.default.findLabelNames(entities, opts.label);
|
|
35
|
-
let story;
|
|
36
|
-
if (!update.name) {
|
|
37
|
-
if (!opts.idonly) spin.stop(true);
|
|
38
|
-
log("Must provide --title");
|
|
39
|
-
} else if (!update.project_id && !update.workflow_state_id) {
|
|
40
|
-
if (!opts.idonly) spin.stop(true);
|
|
41
|
-
log("Must provide --project or --state");
|
|
42
|
-
} else try {
|
|
43
|
-
story = await require_lib_client.default.createStory(update).then((r) => r.data);
|
|
44
|
-
} catch (e) {
|
|
45
|
-
log("Error creating story");
|
|
46
|
-
}
|
|
47
|
-
if (!opts.idonly) spin.stop(true);
|
|
48
|
-
if (story) {
|
|
49
|
-
const hydrateStory = require_lib_stories.default.hydrateStory(entities, story);
|
|
50
|
-
require_lib_stories.default.printDetailedStory(hydrateStory);
|
|
51
|
-
if (opts.gitBranch) require_lib_stories.default.checkoutStoryBranch(hydrateStory);
|
|
52
|
-
else if (opts.gitBranchShort) require_lib_stories.default.checkoutStoryBranch(hydrateStory, `${config.mentionName}/sc-${story.id}/`);
|
|
53
|
-
if (opts.open) (0, child_process.exec)("open " + require_lib_stories.default.storyURL(story));
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
main();
|
|
57
|
-
|
|
58
|
-
//#endregion
|
package/build/bin/short-epic.cjs
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
|
|
3
|
-
const require_lib_spinner = require('../lib/spinner.cjs');
|
|
4
|
-
const require_lib_configure = require('../lib/configure.cjs');
|
|
5
|
-
const require_lib_client = require('../lib/client.cjs');
|
|
6
|
-
const require_lib_stories = require('../lib/stories.cjs');
|
|
7
|
-
let commander = require("commander");
|
|
8
|
-
let child_process = require("child_process");
|
|
9
|
-
|
|
10
|
-
//#region src/bin/short-epic.ts
|
|
11
|
-
const config = require_lib_configure.loadConfig();
|
|
12
|
-
const spin = require_lib_spinner.default();
|
|
13
|
-
const log = console.log;
|
|
14
|
-
const program = new commander.Command().usage("[command] [options]").description("create or view epics");
|
|
15
|
-
program.command("create").description("create a new epic").option("-n, --name [text]", "Set name of epic, required", "").option("-d, --description [text]", "Set description of epic", "").option("-s, --state [name]", "Set state of epic (to do, in progress, done)", "").option("--deadline [date]", "Set deadline for epic (YYYY-MM-DD)", "").option("--planned-start [date]", "Set planned start date (YYYY-MM-DD)", "").option("-o, --owners [id|name]", "Set owners of epic, comma-separated", "").option("-T, --team [id|name]", "Set team of epic", "").option("-l, --label [id|name]", "Set labels of epic, comma-separated", "").option("-M, --milestone [id]", "Set milestone of epic (deprecated, use objectives)", "").option("-I, --idonly", "Print only ID of epic result").option("-O, --open", "Open epic in browser").action(createEpic);
|
|
16
|
-
program.parse(process.argv);
|
|
17
|
-
async function createEpic(options) {
|
|
18
|
-
const entities = await require_lib_stories.default.fetchEntities();
|
|
19
|
-
if (!options.idonly) spin.start();
|
|
20
|
-
const epicData = { name: options.name };
|
|
21
|
-
if (options.description) epicData.description = options.description;
|
|
22
|
-
if (options.state) {
|
|
23
|
-
const stateMap = {
|
|
24
|
-
todo: "to do",
|
|
25
|
-
"to do": "to do",
|
|
26
|
-
inprogress: "in progress",
|
|
27
|
-
"in progress": "in progress",
|
|
28
|
-
done: "done"
|
|
29
|
-
};
|
|
30
|
-
const mappedState = stateMap[options.state.toLowerCase().replace(/[^a-z]/g, "")] || stateMap[options.state.toLowerCase()];
|
|
31
|
-
if (mappedState) epicData.state = mappedState;
|
|
32
|
-
}
|
|
33
|
-
if (options.deadline) epicData.deadline = new Date(options.deadline).toISOString();
|
|
34
|
-
if (options.plannedStart) epicData.planned_start_date = new Date(options.plannedStart).toISOString();
|
|
35
|
-
if (options.owners) epicData.owner_ids = require_lib_stories.default.findOwnerIds(entities, options.owners);
|
|
36
|
-
if (options.team) {
|
|
37
|
-
const group = require_lib_stories.default.findGroup(entities, options.team);
|
|
38
|
-
if (group?.id) epicData.group_ids = [group.id];
|
|
39
|
-
}
|
|
40
|
-
if (options.label) epicData.labels = require_lib_stories.default.findLabelNames(entities, options.label);
|
|
41
|
-
if (options.milestone) epicData.milestone_id = parseInt(options.milestone, 10);
|
|
42
|
-
let epic;
|
|
43
|
-
if (!epicData.name) {
|
|
44
|
-
if (!options.idonly) spin.stop(true);
|
|
45
|
-
log("Must provide --name");
|
|
46
|
-
process.exit(1);
|
|
47
|
-
} else try {
|
|
48
|
-
epic = await require_lib_client.default.createEpic(epicData).then((r) => r.data);
|
|
49
|
-
} catch (e) {
|
|
50
|
-
if (!options.idonly) spin.stop(true);
|
|
51
|
-
log("Error creating epic:", e.message ?? String(e));
|
|
52
|
-
process.exit(1);
|
|
53
|
-
}
|
|
54
|
-
if (!options.idonly) spin.stop(true);
|
|
55
|
-
if (epic) if (options.idonly) log(epic.id);
|
|
56
|
-
else {
|
|
57
|
-
printEpic(epic);
|
|
58
|
-
if (options.open) (0, child_process.exec)(`open https://app.shortcut.com/${config.urlSlug}/epic/${epic.id}`);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
function printEpic(epic) {
|
|
62
|
-
log(`#${epic.id} ${epic.name}`);
|
|
63
|
-
if (epic.description) log(`Description:\t${epic.description}`);
|
|
64
|
-
log(`State:\t\t${epic.state}`);
|
|
65
|
-
if (epic.milestone_id) log(`Milestone:\t${epic.milestone_id}`);
|
|
66
|
-
if (epic.deadline) log(`Deadline:\t${epic.deadline}`);
|
|
67
|
-
if (epic.planned_start_date) log(`Planned Start:\t${epic.planned_start_date}`);
|
|
68
|
-
if (epic.owner_ids && epic.owner_ids.length > 0) log(`Owners:\t\t${epic.owner_ids.join(", ")}`);
|
|
69
|
-
if (epic.group_ids && epic.group_ids.length > 0) log(`Teams:\t\t${epic.group_ids.join(", ")}`);
|
|
70
|
-
if (epic.labels && epic.labels.length > 0) log(`Labels:\t\t${epic.labels.map((l) => l.name).join(", ")}`);
|
|
71
|
-
log(`URL:\t\t${epic.app_url}`);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
//#endregion
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
|
|
3
|
-
const require_lib_spinner = require('../lib/spinner.cjs');
|
|
4
|
-
const require_lib_client = require('../lib/client.cjs');
|
|
5
|
-
let commander = require("commander");
|
|
6
|
-
let chalk = require("chalk");
|
|
7
|
-
chalk = require_rolldown_runtime.__toESM(chalk);
|
|
8
|
-
|
|
9
|
-
//#region src/bin/short-epics.ts
|
|
10
|
-
const log = console.log;
|
|
11
|
-
const spin = require_lib_spinner.default();
|
|
12
|
-
const opts = new commander.Command().description("Display epics available for stories").option("-a, --archived", "List only epics including archived", "").option("-c, --completed", "List only epics that have been completed", "").option("-d, --detailed", "List more details for each epic", "").option("-f, --format [template]", "Format each epic output by template", "").option("-M, --milestone [ID]", "List epics in milestone matching id", "").option("-t, --title [query]", "List epics with name/title containing query", "").option("-s, --started", "List epics that have been started", "").parse(process.argv).opts();
|
|
13
|
-
const main = async () => {
|
|
14
|
-
spin.start();
|
|
15
|
-
const epics = await require_lib_client.default.listEpics(null).then((r) => r.data);
|
|
16
|
-
spin.stop(true);
|
|
17
|
-
const textMatch = new RegExp(opts.title ?? "", "i");
|
|
18
|
-
epics.filter((epic) => {
|
|
19
|
-
return !!`${epic.name} ${epic.name}`.match(textMatch) && !!(opts.milestone ? String(epic.milestone_id) === opts.milestone : true);
|
|
20
|
-
}).map(printItem);
|
|
21
|
-
};
|
|
22
|
-
const printItem = (epic) => {
|
|
23
|
-
if (epic.archived && !opts.archived) return;
|
|
24
|
-
if (!epic.started && opts.started) return;
|
|
25
|
-
if (!epic.completed && opts.completed) return;
|
|
26
|
-
let defaultFormat = `#%id %t\nMilestone:\t%m\nState:\t\t%s\nDeadline:\t%dl\n`;
|
|
27
|
-
defaultFormat += `Points:\t\t%p\nPoints Started: %ps\nPoints Done:\t%pd\nCompletion:\t%c\n`;
|
|
28
|
-
if (epic.archived) defaultFormat += `Archived:\t%ar\n`;
|
|
29
|
-
if (epic.started) defaultFormat += `Started:\t%st\n`;
|
|
30
|
-
if (epic.completed) defaultFormat += `Completed:\t%co\n`;
|
|
31
|
-
if (opts.detailed) defaultFormat += `Description:\t%d\n`;
|
|
32
|
-
log((opts.format || defaultFormat).replace(/%id/, chalk.default.bold(`${epic.id}`)).replace(/%t/, chalk.default.blue(`${epic.name}`)).replace(/%m/, `${epic.milestone_id || "_"}`).replace(/%s/, `${epic.state}`).replace(/%dl/, `${epic.deadline || "_"}`).replace(/%d/, `${epic.description}`).replace(/%p/, `${epic.stats.num_points}`).replace(/%ps/, `${epic.stats.num_points_started}`).replace(/%pd/, `${epic.stats.num_points_done}`).replace(/%c/, `${Math.round(epic.stats.num_points_done / (epic.stats.num_points || 1) * 100)}%`).replace(/%a/, `${epic.archived}`).replace(/%st/, `${epic.started_at}`).replace(/%co/, `${epic.completed_at}`));
|
|
33
|
-
};
|
|
34
|
-
main();
|
|
35
|
-
|
|
36
|
-
//#endregion
|
package/build/bin/short-find.cjs
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
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_package = require('../package.cjs');
|
|
5
|
-
let commander = require("commander");
|
|
6
|
-
let _shortcut_client = require("@shortcut/client");
|
|
7
|
-
let prompt = require("prompt");
|
|
8
|
-
prompt = require_rolldown_runtime.__toESM(prompt);
|
|
9
|
-
|
|
10
|
-
//#region src/bin/short-install.ts
|
|
11
|
-
const extant = require_lib_configure.loadCachedConfig();
|
|
12
|
-
const log = console.log;
|
|
13
|
-
const opts = new commander.Command().version(require_package.version).description("Install access token and other settings for the Shortcut API").option("-f, --force", "Force install/reinstall").option("-r, --refresh", "Refresh the configuration with details from Shortcut.").parse(process.argv).opts();
|
|
14
|
-
const enrichConfigWithMemberDetails = async (config) => {
|
|
15
|
-
log("Fetching user/member details from Shortcut...");
|
|
16
|
-
const member = await new _shortcut_client.ShortcutClient(config.token).getCurrentMemberInfo().then((r) => r.data);
|
|
17
|
-
return {
|
|
18
|
-
mentionName: member.mention_name,
|
|
19
|
-
urlSlug: member.workspace2.url_slug,
|
|
20
|
-
...config
|
|
21
|
-
};
|
|
22
|
-
};
|
|
23
|
-
const main = async () => {
|
|
24
|
-
if (opts.refresh) require_lib_configure.updateConfig(await enrichConfigWithMemberDetails(extant));
|
|
25
|
-
else if (!extant.token || opts.force) {
|
|
26
|
-
const schema = { properties: { token: {
|
|
27
|
-
message: "API Token -> https://app.shortcut.com/xxxx/settings/account/api-tokens",
|
|
28
|
-
required: true
|
|
29
|
-
} } };
|
|
30
|
-
prompt.default.start({ message: "Shortcut" });
|
|
31
|
-
prompt.default.get(schema, async (err, result) => {
|
|
32
|
-
if (err) return log(err);
|
|
33
|
-
const config = await enrichConfigWithMemberDetails(result);
|
|
34
|
-
log("Saving config...");
|
|
35
|
-
if (require_lib_configure.updateConfig(config)) log("Saved config");
|
|
36
|
-
else log("Error saving config");
|
|
37
|
-
});
|
|
38
|
-
} else if (extant.token) log("A configuration/token is already saved. To override, re-run with --force");
|
|
39
|
-
};
|
|
40
|
-
main();
|
|
41
|
-
|
|
42
|
-
//#endregion
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
|
|
3
|
-
const require_lib_spinner = require('../lib/spinner.cjs');
|
|
4
|
-
const require_lib_client = require('../lib/client.cjs');
|
|
5
|
-
let commander = require("commander");
|
|
6
|
-
let chalk = require("chalk");
|
|
7
|
-
chalk = require_rolldown_runtime.__toESM(chalk);
|
|
8
|
-
|
|
9
|
-
//#region src/bin/short-members.ts
|
|
10
|
-
const spin = require_lib_spinner.default("Loading... %s ");
|
|
11
|
-
const log = console.log;
|
|
12
|
-
const opts = new commander.Command().description("Display members available for stories").option("-s, --search [query]", "List members with name containing query", "").option("-d, --disabled", "List members including disabled", "").parse(process.argv).opts();
|
|
13
|
-
const main = async () => {
|
|
14
|
-
spin.start();
|
|
15
|
-
const members = await require_lib_client.default.listMembers(null).then((r) => r.data);
|
|
16
|
-
spin.stop(true);
|
|
17
|
-
const ownerMatch = new RegExp(opts.search ?? "", "i");
|
|
18
|
-
members.filter((o) => {
|
|
19
|
-
return !!`${o.profile.name} ${o.profile.mention_name}`.match(ownerMatch);
|
|
20
|
-
}).map(printMember);
|
|
21
|
-
};
|
|
22
|
-
const printMember = (member) => {
|
|
23
|
-
if (member.disabled && !opts.disabled) return;
|
|
24
|
-
log(chalk.default.bold(`#${member.id}`));
|
|
25
|
-
log(chalk.default.bold("Name: ") + ` ${member.profile.name}`);
|
|
26
|
-
log(chalk.default.bold("Mention Name: ") + ` ${member.profile.mention_name}`);
|
|
27
|
-
log(chalk.default.bold("Role: ") + ` ${member.role}`);
|
|
28
|
-
log(chalk.default.bold("Email: ") + ` ${member.profile.email_address}`);
|
|
29
|
-
if (member.disabled) log(chalk.default.bold("Disabled: ") + ` ${member.disabled}`);
|
|
30
|
-
log();
|
|
31
|
-
};
|
|
32
|
-
main();
|
|
33
|
-
|
|
34
|
-
//#endregion
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
|
|
3
|
-
const require_lib_spinner = require('../lib/spinner.cjs');
|
|
4
|
-
const require_lib_client = require('../lib/client.cjs');
|
|
5
|
-
let commander = require("commander");
|
|
6
|
-
let chalk = require("chalk");
|
|
7
|
-
chalk = require_rolldown_runtime.__toESM(chalk);
|
|
8
|
-
|
|
9
|
-
//#region src/bin/short-projects.ts
|
|
10
|
-
const spin = require_lib_spinner.default();
|
|
11
|
-
const log = console.log;
|
|
12
|
-
const opts = new commander.Command().description("Display projects available for stories").option("-a, --archived", "List only projects including archived", "").option("-d, --detailed", "List more details for each project", "").option("-t, --title [query]", "List projects with name/title containing query", "").parse(process.argv).opts();
|
|
13
|
-
const main = async () => {
|
|
14
|
-
spin.start();
|
|
15
|
-
const projects = await require_lib_client.default.listProjects().then((r) => r.data);
|
|
16
|
-
spin.stop(true);
|
|
17
|
-
const textMatch = new RegExp(opts.title ?? "", "i");
|
|
18
|
-
projects.filter((o) => {
|
|
19
|
-
return !!`${o.name} ${o.name}`.match(textMatch);
|
|
20
|
-
}).map(printItem);
|
|
21
|
-
};
|
|
22
|
-
const printItem = (proj) => {
|
|
23
|
-
if (proj.archived && !opts.archived) return;
|
|
24
|
-
log(chalk.default.bold(`#${proj.id}`) + chalk.default.blue(` ${proj.name}`));
|
|
25
|
-
log(chalk.default.bold("Points: ") + ` ${proj.stats.num_points}`);
|
|
26
|
-
log(chalk.default.bold("Stories: ") + ` ${proj.stats.num_stories}`);
|
|
27
|
-
log(chalk.default.bold("Started: ") + ` ${proj.start_time}`);
|
|
28
|
-
if (proj.archived) log(chalk.default.bold("Archived: ") + ` ${proj.archived}`);
|
|
29
|
-
if (opts.detailed) log(chalk.default.bold("Description: ") + ` ${proj.description}`);
|
|
30
|
-
log();
|
|
31
|
-
};
|
|
32
|
-
main();
|
|
33
|
-
|
|
34
|
-
//#endregion
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
|
|
3
|
-
const require_lib_spinner = require('../lib/spinner.cjs');
|
|
4
|
-
const require_lib_configure = require('../lib/configure.cjs');
|
|
5
|
-
const require_lib_stories = require('../lib/stories.cjs');
|
|
6
|
-
let commander = require("commander");
|
|
7
|
-
|
|
8
|
-
//#region src/bin/short-search.ts
|
|
9
|
-
const spin = require_lib_spinner.default("Finding... %s ");
|
|
10
|
-
const log = console.log;
|
|
11
|
-
const program = new commander.Command().description(`Search through Shortcut stories. Arguments (non-flag/options) will be
|
|
12
|
-
passed to Shortcut story search API as search operators. Passing '%self%' as
|
|
13
|
-
a search operator argument will be replaced by your mention name. Note that
|
|
14
|
-
passing search operators and options (e.g. --owner foobar) will use the
|
|
15
|
-
options as extra filtering in the client.
|
|
16
|
-
|
|
17
|
-
Refer to https://help.shortcut.com/hc/en-us/articles/360000046646-Search-Operators
|
|
18
|
-
for more details about search operators.`).usage("[options] [SEARCH OPERATORS]").option("-a, --archived", "Include archived Stories").option("-c, --created [operator][date]", "Stories created within criteria (operator is one of <|>|=)", "").option("-q, --quiet", "Print only story output, no loading dialog", "").option("-l, --label [id|name]", "Stories with label id/name, by regex", "").option("-o, --owner [name]", "Stories with owner, by regex", "").option("-p, --project [id]", "Stories in project", "").option("-s, --state [id|name]", "Stories in workflow state id/name, by regex", "").option("--epic [id|name]", "Stories in epic id/name, by regex", "").option("-i, --iteration [id|name]", "Stories in iteration id/name, by regex", "").option("-S, --save [name]", "Save search configuration as workspace").option("-t, --text [name]", "Stories with text in name, by regex", "").option("-e, --estimate [operator][number]", "Stories estimated within criteria (operator is one of <|>|=)", "").option("-u, --updated [operator][date]", "Stories updated within criteria (operator is one of <|>|=)", "").option("-y, --type [name]", "Stories of type, by regex", "").option("-r, --sort [field]", "Sort stories by field (accessor[:asc|desc][,next])", "state.position:asc,position:asc").option("-f, --format [template]", "Format each story output by template", "");
|
|
19
|
-
const getWorkspaceOptions = (opts) => {
|
|
20
|
-
const blacklistedKeys = ["save"];
|
|
21
|
-
return Object.entries(opts).filter(([key]) => !blacklistedKeys.includes(key)).reduce((obj, [key, val]) => Object.assign(obj, { [key]: val }), {});
|
|
22
|
-
};
|
|
23
|
-
const main = async () => {
|
|
24
|
-
program.parse(process.argv);
|
|
25
|
-
const opts = program.opts();
|
|
26
|
-
if (!opts.quiet) {
|
|
27
|
-
if (!program.args.length) log("Fetching all stories for search since no search operators were passed ...");
|
|
28
|
-
spin.start();
|
|
29
|
-
}
|
|
30
|
-
let stories = [];
|
|
31
|
-
try {
|
|
32
|
-
stories = await require_lib_stories.default.listStories({
|
|
33
|
-
...opts,
|
|
34
|
-
args: program.args
|
|
35
|
-
});
|
|
36
|
-
} catch (e) {
|
|
37
|
-
log("Error fetching stories:", e);
|
|
38
|
-
}
|
|
39
|
-
if (!opts.quiet) spin.stop(true);
|
|
40
|
-
stories.map(require_lib_stories.default.printFormattedStory(opts));
|
|
41
|
-
if (!opts.save) return;
|
|
42
|
-
const name = opts.save === true ? "default" : opts.save;
|
|
43
|
-
if (require_lib_configure.default.saveWorkspace(name, getWorkspaceOptions(opts))) log("Saved query as %s workspace", name);
|
|
44
|
-
};
|
|
45
|
-
if (process.argv[1]?.includes("short-search")) main();
|
|
46
|
-
|
|
47
|
-
//#endregion
|
|
48
|
-
exports.main = main;
|
|
49
|
-
exports.program = program;
|
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
|
|
3
|
-
const require_lib_spinner = require('../lib/spinner.cjs');
|
|
4
|
-
const require_lib_configure = require('../lib/configure.cjs');
|
|
5
|
-
const require_lib_client = require('../lib/client.cjs');
|
|
6
|
-
const require_lib_stories = require('../lib/stories.cjs');
|
|
7
|
-
let commander = require("commander");
|
|
8
|
-
let debug = require("debug");
|
|
9
|
-
debug = require_rolldown_runtime.__toESM(debug);
|
|
10
|
-
let path = require("path");
|
|
11
|
-
path = require_rolldown_runtime.__toESM(path);
|
|
12
|
-
let fs = require("fs");
|
|
13
|
-
fs = require_rolldown_runtime.__toESM(fs);
|
|
14
|
-
let os = require("os");
|
|
15
|
-
os = require_rolldown_runtime.__toESM(os);
|
|
16
|
-
let child_process = require("child_process");
|
|
17
|
-
let chalk = require("chalk");
|
|
18
|
-
chalk = require_rolldown_runtime.__toESM(chalk);
|
|
19
|
-
let https = require("https");
|
|
20
|
-
https = require_rolldown_runtime.__toESM(https);
|
|
21
|
-
|
|
22
|
-
//#region src/bin/short-story.ts
|
|
23
|
-
const config = require_lib_configure.loadConfig();
|
|
24
|
-
const spin = require_lib_spinner.default();
|
|
25
|
-
const log = console.log;
|
|
26
|
-
const logError = console.error;
|
|
27
|
-
const debug$1 = (0, debug.default)("short");
|
|
28
|
-
const program = new commander.Command().usage("[options] <id>").description("Update and/or display story details").option("-a, --archived", "Update story as archived").option("-c, --comment [text]", "Add comment to story", "").option("-d, --description [text]", "Update description of story", "").option("-D, --download", "Download all attached files", "").option("--download-dir [path]", "Directory to download files to", ".").option("-e, --estimate [number]", "Update estimate of story", "").option("--epic [id|name]", "Set epic of story").option("-i, --iteration [id|name]", "Set iteration of story").option("-f, --format [template]", "Format the story output by template", "").option("--from-git", "Fetch story parsed by ID from current git branch").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, --idonly", "Print only ID of story results", "").option("-l, --label [id|name]", "Stories with label id/name, by regex", "").option("--move-after [id]", "Move story to position below story ID").option("--move-before [id]", "Move story to position above story ID").option("--move-down [n]", "Move story position downward by n stories").option("--move-up [n]", "Move story position upward by n stories").option("-o, --owners [id|name]", "Update owners of story, comma-separated", "").option("-O, --open", "Open story in browser").option("--oe, --open-epic", "Open story's epic in browser").option("--oi, --open-iteration", "Open story's iteration in browser").option("--op, --open-project", "Open story's project in browser").option("-q, --quiet", "Print only story output, no loading dialog", "").option("-s, --state [id|name]", "Update workflow state of story", "").option("-t, --title [text]", "Update title/name of story", "").option("-T, --team [id|name]", "Update team/group of story", "").option("--task [text]", "Create new task on story").option("--task-complete [text]", "Toggle completion of task on story matching text").option("-y, --type [name]", "Update type of story", "").parse(process.argv);
|
|
29
|
-
const opts = program.opts();
|
|
30
|
-
const main = async () => {
|
|
31
|
-
const entities = await require_lib_stories.default.fetchEntities();
|
|
32
|
-
if (!(opts.idonly || opts.quiet)) spin.start();
|
|
33
|
-
debug$1("constructing story update");
|
|
34
|
-
const update = {};
|
|
35
|
-
if (opts.archived) update.archived = true;
|
|
36
|
-
if (opts.state) update.workflow_state_id = require_lib_stories.default.findState(entities, opts.state)?.id;
|
|
37
|
-
if (opts.estimate) update.estimate = parseInt(opts.estimate, 10);
|
|
38
|
-
if (opts.title) update.name = opts.title;
|
|
39
|
-
if (opts.description) update.description = `${opts.description}`;
|
|
40
|
-
if (opts.type) {
|
|
41
|
-
const storyTypes = [
|
|
42
|
-
"feature",
|
|
43
|
-
"bug",
|
|
44
|
-
"chore"
|
|
45
|
-
];
|
|
46
|
-
const typeMatch = new RegExp(opts.type, "i");
|
|
47
|
-
update.story_type = storyTypes.find((t) => t.match(typeMatch));
|
|
48
|
-
}
|
|
49
|
-
if (opts.owners) update.owner_ids = require_lib_stories.default.findOwnerIds(entities, opts.owners);
|
|
50
|
-
if (opts.epic) update.epic_id = require_lib_stories.default.findEpic(entities, opts.epic)?.id;
|
|
51
|
-
if (opts.iteration) update.iteration_id = require_lib_stories.default.findIteration(entities, opts.iteration)?.id;
|
|
52
|
-
if (opts.label) update.labels = require_lib_stories.default.findLabelNames(entities, opts.label);
|
|
53
|
-
if (opts.team) update.group_id = require_lib_stories.default.findGroup(entities, opts.team)?.id;
|
|
54
|
-
const hasPositionUpdate = opts.moveAfter !== void 0 || opts.moveBefore !== void 0 || opts.moveDown !== void 0 || opts.moveUp !== void 0;
|
|
55
|
-
const hasUpdate = Object.keys(update).length > 0 || hasPositionUpdate;
|
|
56
|
-
debug$1("constructed story update", update);
|
|
57
|
-
const gitID = [];
|
|
58
|
-
if (opts.fromGit || !program.args.length) {
|
|
59
|
-
debug$1("fetching story ID from git");
|
|
60
|
-
let branch = "";
|
|
61
|
-
try {
|
|
62
|
-
branch = (0, child_process.execSync)("git branch").toString("utf-8");
|
|
63
|
-
} catch (e) {
|
|
64
|
-
debug$1(e);
|
|
65
|
-
}
|
|
66
|
-
if (branch.match(/\*.*[0-9]+/)) {
|
|
67
|
-
debug$1("parsing story ID from git branch:", branch);
|
|
68
|
-
const id = parseInt(branch.match(/\*.*/)[0].match(/\/(ch|sc-)([0-9]+)/)[2], 10);
|
|
69
|
-
debug$1("parsed story ID from git branch:", id);
|
|
70
|
-
if (id) gitID.push(id.toString());
|
|
71
|
-
} else {
|
|
72
|
-
stopSpinner();
|
|
73
|
-
logError("No story ID argument present or found in git branch");
|
|
74
|
-
process.exit(2);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
program.args.map((a) => (a.match(/\d+/) || [])[0]).concat(gitID).map(async (_id) => {
|
|
78
|
-
const id = parseInt(_id, 10);
|
|
79
|
-
let story;
|
|
80
|
-
try {
|
|
81
|
-
if (opts.comment) {
|
|
82
|
-
debug$1("request comment create");
|
|
83
|
-
await require_lib_client.default.createStoryComment(id, { text: opts.comment });
|
|
84
|
-
debug$1("response comment create");
|
|
85
|
-
}
|
|
86
|
-
} catch (e) {
|
|
87
|
-
stopSpinner();
|
|
88
|
-
log("Error creating comment", id);
|
|
89
|
-
process.exit(3);
|
|
90
|
-
}
|
|
91
|
-
try {
|
|
92
|
-
if (opts.task) {
|
|
93
|
-
debug$1("request task create");
|
|
94
|
-
await require_lib_client.default.createTask(id, { description: opts.task });
|
|
95
|
-
debug$1("response task create");
|
|
96
|
-
}
|
|
97
|
-
} catch (e) {
|
|
98
|
-
stopSpinner();
|
|
99
|
-
log("Error creating task", id);
|
|
100
|
-
process.exit(3);
|
|
101
|
-
}
|
|
102
|
-
try {
|
|
103
|
-
debug$1("request story");
|
|
104
|
-
story = await require_lib_client.default.getStory(id).then((r) => r.data);
|
|
105
|
-
debug$1("response story");
|
|
106
|
-
} catch (e) {
|
|
107
|
-
stopSpinner();
|
|
108
|
-
logError("Error fetching story", id);
|
|
109
|
-
process.exit(4);
|
|
110
|
-
}
|
|
111
|
-
try {
|
|
112
|
-
if (opts.taskComplete) {
|
|
113
|
-
debug$1("calculating task(s) to complete");
|
|
114
|
-
const descMatch = new RegExp(opts.taskComplete, "i");
|
|
115
|
-
const tasks = story.tasks.filter((t) => t.description.match(descMatch));
|
|
116
|
-
const updatedTaskIds = tasks.map((t) => t.id);
|
|
117
|
-
debug$1("request tasks complete", updatedTaskIds);
|
|
118
|
-
await Promise.all(tasks.map((t) => require_lib_client.default.updateTask(id, t.id, { complete: !t.complete })));
|
|
119
|
-
debug$1("response tasks complete");
|
|
120
|
-
story.tasks = story.tasks.map((t) => {
|
|
121
|
-
if (updatedTaskIds.indexOf(t.id) > -1) t.complete = !t.complete;
|
|
122
|
-
return t;
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
} catch (e) {
|
|
126
|
-
stopSpinner();
|
|
127
|
-
log("Error updating tasks", e);
|
|
128
|
-
process.exit(3);
|
|
129
|
-
}
|
|
130
|
-
try {
|
|
131
|
-
if (hasUpdate) {
|
|
132
|
-
if (hasPositionUpdate) {
|
|
133
|
-
debug$1("calculating move up/down");
|
|
134
|
-
const siblings = await require_lib_stories.default.listStories({
|
|
135
|
-
state: story.workflow_state_id.toString(),
|
|
136
|
-
sort: "state.position:asc,position:asc"
|
|
137
|
-
});
|
|
138
|
-
const siblingIds = siblings.map((s) => s.id);
|
|
139
|
-
const storyIndex = siblingIds.indexOf(~~id);
|
|
140
|
-
if (opts.moveAfter) update.after_id = ~~opts.moveAfter;
|
|
141
|
-
else if (opts.moveBefore) update.before_id = ~~opts.moveBefore;
|
|
142
|
-
else if (opts.moveUp) update.before_id = siblingIds[Math.max(0, storyIndex - (~~opts.moveUp || 1))];
|
|
143
|
-
else if (opts.moveDown) update.after_id = siblingIds[Math.min(siblings.length - 1, storyIndex + (~~opts.moveDown || 1))];
|
|
144
|
-
debug$1("constructed story position update", update);
|
|
145
|
-
}
|
|
146
|
-
debug$1("request story update");
|
|
147
|
-
const changed = await require_lib_client.default.updateStory(id, update);
|
|
148
|
-
debug$1("response story update");
|
|
149
|
-
story = Object.assign({}, story, changed);
|
|
150
|
-
}
|
|
151
|
-
} catch (e) {
|
|
152
|
-
stopSpinner();
|
|
153
|
-
logError("Error updating story", id);
|
|
154
|
-
process.exit(5);
|
|
155
|
-
}
|
|
156
|
-
if (story) story = require_lib_stories.default.hydrateStory(entities, story);
|
|
157
|
-
if (!opts.idonly) spin.stop(true);
|
|
158
|
-
if (story) {
|
|
159
|
-
printStory(story, entities);
|
|
160
|
-
if (opts.open) openURL(require_lib_stories.default.storyURL(story));
|
|
161
|
-
if (opts.openEpic) {
|
|
162
|
-
if (!story.epic_id) {
|
|
163
|
-
logError("This story is not part of an epic.");
|
|
164
|
-
process.exit(21);
|
|
165
|
-
}
|
|
166
|
-
openURL(require_lib_stories.default.buildURL("epic", story.epic_id));
|
|
167
|
-
}
|
|
168
|
-
if (opts.openIteration) {
|
|
169
|
-
if (!story.iteration_id) {
|
|
170
|
-
logError("This story is not part of an iteration.");
|
|
171
|
-
process.exit(22);
|
|
172
|
-
}
|
|
173
|
-
openURL(require_lib_stories.default.buildURL("iteration", story.iteration_id));
|
|
174
|
-
}
|
|
175
|
-
if (opts.openProject) openURL(require_lib_stories.default.buildURL("project", story.project_id));
|
|
176
|
-
}
|
|
177
|
-
if (opts.download) downloadFiles(story);
|
|
178
|
-
if (story && opts.gitBranch) {
|
|
179
|
-
if (!config.mentionName) {
|
|
180
|
-
stopSpinner();
|
|
181
|
-
require_lib_stories.default.checkoutStoryBranch(story, `${story.story_type}-${story.id}-`);
|
|
182
|
-
logError("Error creating story branch in Shortcut format");
|
|
183
|
-
logError("Please run: \"short install --force\" to add your mention name to the config.");
|
|
184
|
-
process.exit(10);
|
|
185
|
-
}
|
|
186
|
-
require_lib_stories.default.checkoutStoryBranch(story);
|
|
187
|
-
} else if (story && opts.gitBranchShort) require_lib_stories.default.checkoutStoryBranch(story, `${config.mentionName}/sc-${story.id}/`);
|
|
188
|
-
});
|
|
189
|
-
stopSpinner();
|
|
190
|
-
};
|
|
191
|
-
const openURL = (url) => {
|
|
192
|
-
(0, child_process.execSync)(`${os.default.platform() === "darwin" ? "open" : "xdg-open"} '${url}'`);
|
|
193
|
-
};
|
|
194
|
-
const stopSpinner = () => {
|
|
195
|
-
if (!(opts.idonly || opts.quiet)) spin.stop(true);
|
|
196
|
-
};
|
|
197
|
-
const downloadFiles = (story) => story.files.map((file) => {
|
|
198
|
-
https.default.get(require_lib_stories.default.fileURL(file), (res) => {
|
|
199
|
-
const filePath = path.default.join(opts.downloadDir ?? ".", file.name);
|
|
200
|
-
log(chalk.default.bold("Downloading file to: ") + filePath);
|
|
201
|
-
const stream = fs.default.createWriteStream(filePath);
|
|
202
|
-
res.pipe(stream);
|
|
203
|
-
stream.on("finish", () => stream.close());
|
|
204
|
-
});
|
|
205
|
-
});
|
|
206
|
-
const printStory = (story, entities) => {
|
|
207
|
-
if (opts.idonly) return log(story.id);
|
|
208
|
-
if (opts.format) return require_lib_stories.default.printFormattedStory(opts)(story);
|
|
209
|
-
require_lib_stories.default.printDetailedStory(story, entities);
|
|
210
|
-
};
|
|
211
|
-
main();
|
|
212
|
-
|
|
213
|
-
//#endregion
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
|
|
3
|
-
const require_lib_spinner = require('../lib/spinner.cjs');
|
|
4
|
-
const require_lib_client = require('../lib/client.cjs');
|
|
5
|
-
let commander = require("commander");
|
|
6
|
-
let chalk = require("chalk");
|
|
7
|
-
chalk = require_rolldown_runtime.__toESM(chalk);
|
|
8
|
-
|
|
9
|
-
//#region src/bin/short-workflows.ts
|
|
10
|
-
const spin = require_lib_spinner.default();
|
|
11
|
-
const log = console.log;
|
|
12
|
-
const opts = new commander.Command().description("Display workflows/states available for stories").option("-s, --search [query]", "List states containing query", "").parse(process.argv).opts();
|
|
13
|
-
const main = async () => {
|
|
14
|
-
spin.start();
|
|
15
|
-
const wfs = await require_lib_client.default.listWorkflows().then((r) => r.data);
|
|
16
|
-
spin.stop(true);
|
|
17
|
-
wfs.map(printWf);
|
|
18
|
-
};
|
|
19
|
-
const printWf = (wf) => {
|
|
20
|
-
log(chalk.default.bold(`#${wf.id}`) + ` ${wf.name}`);
|
|
21
|
-
log(" == States:");
|
|
22
|
-
wf.states.map(printWfState);
|
|
23
|
-
};
|
|
24
|
-
const printWfState = (state) => {
|
|
25
|
-
if (!state.name.match(new RegExp(opts.search ?? "", "i"))) return;
|
|
26
|
-
log(chalk.default.bold(` #${state.id}`) + ` ${state.name}`);
|
|
27
|
-
log(` Type: \t${state.type}`);
|
|
28
|
-
log(` Stories:\t${state.num_stories}`);
|
|
29
|
-
};
|
|
30
|
-
main();
|
|
31
|
-
|
|
32
|
-
//#endregion
|