gittable 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +459 -0
  3. package/cli.js +342 -0
  4. package/commands/add.js +159 -0
  5. package/commands/blame.js +33 -0
  6. package/commands/branch.js +234 -0
  7. package/commands/checkout.js +43 -0
  8. package/commands/cherry-pick.js +104 -0
  9. package/commands/clean.js +71 -0
  10. package/commands/clone.js +76 -0
  11. package/commands/commit.js +82 -0
  12. package/commands/config.js +171 -0
  13. package/commands/diff.js +30 -0
  14. package/commands/fetch.js +76 -0
  15. package/commands/grep.js +42 -0
  16. package/commands/init.js +45 -0
  17. package/commands/log.js +38 -0
  18. package/commands/merge.js +69 -0
  19. package/commands/mv.js +40 -0
  20. package/commands/pull.js +74 -0
  21. package/commands/push.js +97 -0
  22. package/commands/rebase.js +134 -0
  23. package/commands/remote.js +236 -0
  24. package/commands/restore.js +76 -0
  25. package/commands/revert.js +63 -0
  26. package/commands/rm.js +57 -0
  27. package/commands/show.js +47 -0
  28. package/commands/stash.js +201 -0
  29. package/commands/status.js +21 -0
  30. package/commands/sync.js +98 -0
  31. package/commands/tag.js +153 -0
  32. package/commands/undo.js +200 -0
  33. package/commands/uninit.js +57 -0
  34. package/index.d.ts +56 -0
  35. package/index.js +55 -0
  36. package/lib/commit/build-commit.js +64 -0
  37. package/lib/commit/get-previous-commit.js +15 -0
  38. package/lib/commit/questions.js +226 -0
  39. package/lib/config/read-config-file.js +54 -0
  40. package/lib/git/exec.js +222 -0
  41. package/lib/ui/ascii.js +154 -0
  42. package/lib/ui/banner.js +80 -0
  43. package/lib/ui/status-display.js +90 -0
  44. package/lib/ui/table.js +76 -0
  45. package/lib/utils/email-prompt.js +62 -0
  46. package/lib/utils/logger.js +47 -0
  47. package/lib/utils/spinner.js +57 -0
  48. package/lib/utils/terminal-link.js +55 -0
  49. package/lib/versions.js +17 -0
  50. package/package.json +73 -0
  51. package/standalone.js +24 -0
@@ -0,0 +1,234 @@
1
+ const clack = require('@clack/prompts');
2
+ const chalk = require('chalk');
3
+ const { execGit, getBranches, getCurrentBranch } = require('../lib/git/exec');
4
+ const { createTable } = require('../lib/ui/table');
5
+ const { showBanner } = require('../lib/ui/banner');
6
+
7
+ const listBranches = async () => {
8
+ const branches = getBranches();
9
+ const _current = getCurrentBranch();
10
+
11
+ const rows = branches.local.map((branch) => [
12
+ branch.current ? chalk.green('*') : ' ',
13
+ branch.current ? chalk.green.bold(branch.name) : branch.name,
14
+ branch.upstream ? chalk.gray(`[${branch.upstream}]`) : chalk.dim('(no upstream)'),
15
+ ]);
16
+
17
+ console.log(createTable(['', 'Branch', 'Upstream'], rows));
18
+
19
+ if (branches.remote.length > 0) {
20
+ console.log(chalk.gray('\nRemote branches:'));
21
+ for (const branch of branches.remote) {
22
+ console.log(chalk.gray(` ${branch.name}`));
23
+ }
24
+ }
25
+ };
26
+
27
+ const createBranch = async (name) => {
28
+ if (!name) {
29
+ name = await clack.text({
30
+ message: chalk.cyan('Branch name:'),
31
+ placeholder: 'feature/new-feature',
32
+ });
33
+
34
+ if (clack.isCancel(name)) {
35
+ clack.cancel(chalk.yellow('Cancelled'));
36
+ return;
37
+ }
38
+ }
39
+
40
+ const spinner = clack.spinner();
41
+ spinner.start(`Creating branch ${name}`);
42
+
43
+ const result = execGit(`checkout -b ${name}`, { silent: true });
44
+ spinner.stop();
45
+
46
+ if (result.success) {
47
+ clack.outro(chalk.green.bold(`Branch ${name} created and checked out`));
48
+ } else {
49
+ clack.cancel(chalk.red('Failed to create branch'));
50
+ console.error(result.error);
51
+ process.exit(1);
52
+ }
53
+ };
54
+
55
+ const checkoutBranch = async (name) => {
56
+ const branches = getBranches();
57
+
58
+ if (!name) {
59
+ const options = branches.local.map((branch) => ({
60
+ value: branch.name,
61
+ label: branch.current ? chalk.green(`* ${branch.name}`) : branch.name,
62
+ }));
63
+
64
+ name = await clack.select({
65
+ message: chalk.cyan('Select branch to checkout:'),
66
+ options,
67
+ });
68
+
69
+ if (clack.isCancel(name)) {
70
+ clack.cancel(chalk.yellow('Cancelled'));
71
+ return;
72
+ }
73
+ }
74
+
75
+ const spinner = clack.spinner();
76
+ spinner.start(`Checking out ${name}`);
77
+
78
+ const result = execGit(`checkout ${name}`, { silent: true });
79
+ spinner.stop();
80
+
81
+ if (result.success) {
82
+ clack.outro(chalk.green.bold(`Switched to branch ${name}`));
83
+ } else {
84
+ clack.cancel(chalk.red('Failed to checkout branch'));
85
+ console.error(result.error);
86
+ process.exit(1);
87
+ }
88
+ };
89
+
90
+ const deleteBranch = async (name) => {
91
+ const branches = getBranches();
92
+ const current = getCurrentBranch();
93
+
94
+ if (!name) {
95
+ const options = branches.local
96
+ .filter((branch) => !branch.current)
97
+ .map((branch) => ({
98
+ value: branch.name,
99
+ label: branch.name,
100
+ }));
101
+
102
+ if (options.length === 0) {
103
+ clack.cancel(chalk.yellow('No branches to delete'));
104
+ return;
105
+ }
106
+
107
+ name = await clack.select({
108
+ message: chalk.cyan('Select branch to delete:'),
109
+ options,
110
+ });
111
+
112
+ if (clack.isCancel(name)) {
113
+ clack.cancel(chalk.yellow('Cancelled'));
114
+ return;
115
+ }
116
+ }
117
+
118
+ if (name === current) {
119
+ clack.cancel(chalk.red('Cannot delete current branch'));
120
+ process.exit(1);
121
+ }
122
+
123
+ const confirm = await clack.confirm({
124
+ message: chalk.yellow(`Delete branch ${name}?`),
125
+ initialValue: false,
126
+ });
127
+
128
+ if (clack.isCancel(confirm) || !confirm) {
129
+ clack.cancel(chalk.yellow('Cancelled'));
130
+ return;
131
+ }
132
+
133
+ const spinner = clack.spinner();
134
+ spinner.start(`Deleting branch ${name}`);
135
+
136
+ const result = execGit(`branch -d ${name}`, { silent: true });
137
+ spinner.stop();
138
+
139
+ if (result.success) {
140
+ clack.outro(chalk.green.bold(`Branch ${name} deleted`));
141
+ } else {
142
+ // Try force delete
143
+ const forceConfirm = await clack.confirm({
144
+ message: chalk.yellow('Branch not fully merged. Force delete?'),
145
+ initialValue: false,
146
+ });
147
+
148
+ if (forceConfirm) {
149
+ const forceResult = execGit(`branch -D ${name}`, { silent: true });
150
+ if (forceResult.success) {
151
+ clack.outro(chalk.green.bold(`Branch ${name} force deleted`));
152
+ } else {
153
+ clack.cancel(chalk.red('Failed to delete branch'));
154
+ console.error(forceResult.error);
155
+ process.exit(1);
156
+ }
157
+ } else {
158
+ clack.cancel(chalk.yellow('Cancelled'));
159
+ }
160
+ }
161
+ };
162
+
163
+ module.exports = async (args) => {
164
+ const action = args[0];
165
+
166
+ // If no action provided, show interactive menu
167
+ if (!action) {
168
+ showBanner('BRANCH');
169
+
170
+ // Check if TTY is available for interactive prompts
171
+ if (!process.stdin.isTTY) {
172
+ clack.cancel(chalk.red('Interactive mode required'));
173
+ console.log(chalk.yellow('This command requires interactive input.'));
174
+ console.log(chalk.gray('Available actions:'));
175
+ console.log(chalk.gray(' - gittable branch list'));
176
+ console.log(chalk.gray(' - gittable branch create <name>'));
177
+ console.log(chalk.gray(' - gittable branch checkout <name>'));
178
+ console.log(chalk.gray(' - gittable branch delete <name>'));
179
+ process.exit(1);
180
+ }
181
+
182
+ const selectedAction = await clack.select({
183
+ message: chalk.cyan('What would you like to do?'),
184
+ options: [
185
+ { value: 'list', label: chalk.cyan('List branches') },
186
+ { value: 'create', label: chalk.green('Create new branch') },
187
+ { value: 'checkout', label: chalk.yellow('Checkout branch') },
188
+ { value: 'delete', label: chalk.red('Delete branch') },
189
+ ],
190
+ });
191
+
192
+ if (clack.isCancel(selectedAction)) {
193
+ clack.cancel(chalk.yellow('Cancelled'));
194
+ return;
195
+ }
196
+
197
+ // Recursively call with the selected action
198
+ return module.exports([selectedAction, ...args.slice(1)]);
199
+ }
200
+
201
+ if (action === 'list' || action === 'ls') {
202
+ showBanner('BRANCH');
203
+ console.log(`${chalk.gray('├')} ${chalk.cyan.bold('Branch List')}`);
204
+ await listBranches();
205
+ clack.outro(chalk.green.bold('Done'));
206
+ return;
207
+ }
208
+
209
+ if (action === 'create' || action === 'new') {
210
+ showBanner('BRANCH');
211
+ console.log(`${chalk.gray('├')} ${chalk.cyan.bold('Create Branch')}`);
212
+ await createBranch(args[1]);
213
+ return;
214
+ }
215
+
216
+ if (action === 'checkout' || action === 'co') {
217
+ showBanner('BRANCH');
218
+ console.log(`${chalk.gray('├')} ${chalk.cyan.bold('Checkout Branch')}`);
219
+ await checkoutBranch(args[1]);
220
+ return;
221
+ }
222
+
223
+ if (action === 'delete' || action === 'del' || action === 'rm') {
224
+ showBanner('BRANCH');
225
+ console.log(`${chalk.gray('├')} ${chalk.cyan.bold('Delete Branch')}`);
226
+ await deleteBranch(args[1]);
227
+ return;
228
+ }
229
+
230
+ // Default: try to checkout
231
+ showBanner('BRANCH');
232
+ console.log(`${chalk.gray('├')} ${chalk.cyan.bold('Checkout Branch')}`);
233
+ await checkoutBranch(action);
234
+ };
@@ -0,0 +1,43 @@
1
+ const clack = require('@clack/prompts');
2
+ const chalk = require('chalk');
3
+ const { execGit } = require('../lib/git/exec');
4
+ const { showBanner } = require('../lib/ui/banner');
5
+
6
+ module.exports = async (args) => {
7
+ showBanner('CHECKOUT');
8
+ console.log(`${chalk.gray('├')} ${chalk.cyan.bold('Checkout Files')}`);
9
+
10
+ const files = args.filter((arg) => !arg.startsWith('--'));
11
+ const from =
12
+ args.find((arg) => arg.startsWith('--source='))?.split('=')[1] ||
13
+ args.find((arg) => arg.startsWith('--ours'))
14
+ ? '--ours'
15
+ : args.find((arg) => arg.startsWith('--theirs'))
16
+ ? '--theirs'
17
+ : null;
18
+
19
+ if (files.length === 0) {
20
+ clack.cancel(chalk.yellow('No files specified'));
21
+ return;
22
+ }
23
+
24
+ const spinner = clack.spinner();
25
+ spinner.start(`Checking out ${files.length} file(s)`);
26
+
27
+ let command = 'checkout';
28
+ if (from) {
29
+ command += ` ${from}`;
30
+ }
31
+ command += ` -- ${files.join(' ')}`;
32
+
33
+ const result = execGit(command, { silent: false });
34
+ spinner.stop();
35
+
36
+ if (result.success) {
37
+ clack.outro(chalk.green.bold(`Checked out ${files.length} file(s)`));
38
+ } else {
39
+ clack.cancel(chalk.red('Failed to checkout files'));
40
+ console.error(result.error);
41
+ process.exit(1);
42
+ }
43
+ };
@@ -0,0 +1,104 @@
1
+ const clack = require('@clack/prompts');
2
+ const chalk = require('chalk');
3
+ const { execGit } = require('../lib/git/exec');
4
+ const { showBanner } = require('../lib/ui/banner');
5
+
6
+ module.exports = async (args) => {
7
+ showBanner('CHERRY-PICK');
8
+ console.log(`${chalk.gray('├')} ${chalk.cyan.bold('Cherry-pick Commit')}`);
9
+
10
+ let commit = args[0];
11
+ const noCommit = args.includes('--no-commit') || args.includes('-n');
12
+ const continuePick = args.includes('--continue');
13
+ const abortPick = args.includes('--abort');
14
+
15
+ if (continuePick) {
16
+ const spinner = clack.spinner();
17
+ spinner.start('Continuing cherry-pick');
18
+ const result = execGit('cherry-pick --continue', { silent: false });
19
+ spinner.stop();
20
+
21
+ if (result.success) {
22
+ clack.outro(chalk.green.bold('Cherry-pick continued'));
23
+ } else {
24
+ clack.cancel(chalk.red('Cherry-pick continue failed'));
25
+ console.error(result.error);
26
+ process.exit(1);
27
+ }
28
+ return;
29
+ }
30
+
31
+ if (abortPick) {
32
+ if (!process.stdin.isTTY) {
33
+ clack.cancel(chalk.red('Interactive mode required'));
34
+ console.log(chalk.yellow('This command requires interactive confirmation.'));
35
+ process.exit(1);
36
+ }
37
+
38
+ const confirm = await clack.confirm({
39
+ message: chalk.yellow('Abort cherry-pick? This will lose any progress.'),
40
+ initialValue: false,
41
+ });
42
+
43
+ if (clack.isCancel(confirm) || !confirm) {
44
+ clack.cancel(chalk.yellow('Cancelled'));
45
+ return;
46
+ }
47
+
48
+ const spinner = clack.spinner();
49
+ spinner.start('Aborting cherry-pick');
50
+ const result = execGit('cherry-pick --abort', { silent: true });
51
+ spinner.stop();
52
+
53
+ if (result.success) {
54
+ clack.outro(chalk.green.bold('Cherry-pick aborted'));
55
+ } else {
56
+ clack.cancel(chalk.red('Failed to abort cherry-pick'));
57
+ console.error(result.error);
58
+ process.exit(1);
59
+ }
60
+ return;
61
+ }
62
+
63
+ if (!commit) {
64
+ if (!process.stdin.isTTY) {
65
+ clack.cancel(chalk.red('Interactive mode required'));
66
+ console.log(chalk.yellow('This command requires interactive input.'));
67
+ console.log(chalk.gray('Please provide a commit hash: gittable cherry-pick <commit>'));
68
+ process.exit(1);
69
+ }
70
+
71
+ commit = await clack.text({
72
+ message: chalk.cyan('Commit hash to cherry-pick:'),
73
+ placeholder: 'abc1234',
74
+ });
75
+
76
+ if (clack.isCancel(commit)) {
77
+ clack.cancel(chalk.yellow('Cancelled'));
78
+ return;
79
+ }
80
+ }
81
+
82
+ const spinner = clack.spinner();
83
+ spinner.start(`Cherry-picking commit ${commit}`);
84
+
85
+ let command = 'cherry-pick';
86
+ if (noCommit) {
87
+ command += ' --no-commit';
88
+ }
89
+ command += ` ${commit}`;
90
+
91
+ const result = execGit(command, { silent: false });
92
+ spinner.stop();
93
+
94
+ if (result.success) {
95
+ clack.outro(chalk.green.bold(`Cherry-picked commit ${commit}`));
96
+ } else {
97
+ clack.cancel(chalk.red('Cherry-pick failed'));
98
+ console.error(result.error);
99
+ console.log(chalk.yellow('\nYou may need to resolve conflicts manually'));
100
+ console.log(chalk.gray('Use "gittable cherry-pick --continue" to continue after resolving'));
101
+ console.log(chalk.gray('Use "gittable cherry-pick --abort" to abort'));
102
+ process.exit(1);
103
+ }
104
+ };
@@ -0,0 +1,71 @@
1
+ const clack = require('@clack/prompts');
2
+ const chalk = require('chalk');
3
+ const { execGit, getStatus } = require('../lib/git/exec');
4
+ const { showBanner } = require('../lib/ui/banner');
5
+
6
+ module.exports = async (args) => {
7
+ showBanner('CLEAN');
8
+ console.log(`${chalk.gray('├')} ${chalk.cyan.bold('Clean Untracked Files')}`);
9
+
10
+ const dryRun = args.includes('--dry-run') || args.includes('-n');
11
+ const force = args.includes('--force') || args.includes('-f');
12
+ const directories = args.includes('--dir') || args.includes('-d');
13
+ const interactive = args.includes('--interactive') || args.includes('-i');
14
+
15
+ const status = getStatus();
16
+ if (!status) {
17
+ clack.cancel(chalk.red('Failed to get repository status'));
18
+ process.exit(1);
19
+ }
20
+
21
+ if (status.untracked.length === 0) {
22
+ clack.cancel(chalk.yellow('No untracked files to clean'));
23
+ return;
24
+ }
25
+
26
+ if (dryRun) {
27
+ console.log(chalk.yellow('\nFiles that would be removed:'));
28
+ for (const file of status.untracked) {
29
+ console.log(chalk.gray(` ${file}`));
30
+ }
31
+ clack.outro(chalk.green.bold('Dry run complete'));
32
+ return;
33
+ }
34
+
35
+ if (!force && !interactive) {
36
+ const confirm = await clack.confirm({
37
+ message: chalk.yellow(`Remove ${status.untracked.length} untracked file(s)?`),
38
+ initialValue: false,
39
+ });
40
+
41
+ if (clack.isCancel(confirm) || !confirm) {
42
+ clack.cancel(chalk.yellow('Cancelled'));
43
+ return;
44
+ }
45
+ }
46
+
47
+ const spinner = clack.spinner();
48
+ spinner.start('Cleaning untracked files');
49
+
50
+ let command = 'clean';
51
+ if (force) {
52
+ command += ' -f';
53
+ }
54
+ if (directories) {
55
+ command += ' -d';
56
+ }
57
+ if (interactive) {
58
+ command += ' -i';
59
+ }
60
+
61
+ const result = execGit(command, { silent: false });
62
+ spinner.stop();
63
+
64
+ if (result.success) {
65
+ clack.outro(chalk.green.bold('Clean completed'));
66
+ } else {
67
+ clack.cancel(chalk.red('Clean failed'));
68
+ console.error(result.error);
69
+ process.exit(1);
70
+ }
71
+ };
@@ -0,0 +1,76 @@
1
+ const clack = require('@clack/prompts');
2
+ const chalk = require('chalk');
3
+ const { execGit } = require('../lib/git/exec');
4
+ const { showBanner } = require('../lib/ui/banner');
5
+
6
+ module.exports = async (args) => {
7
+ showBanner('CLONE');
8
+ console.log(`${chalk.gray('├')} ${chalk.cyan.bold('Clone Repository')}`);
9
+
10
+ let url = args[0];
11
+ let directory = args[1];
12
+
13
+ if (!url) {
14
+ if (!process.stdin.isTTY) {
15
+ clack.cancel(chalk.red('Interactive mode required'));
16
+ console.log(chalk.yellow('This command requires interactive input.'));
17
+ console.log(chalk.gray('Please provide a repository URL: gittable clone <url> [directory]'));
18
+ process.exit(1);
19
+ }
20
+
21
+ url = await clack.text({
22
+ message: chalk.cyan('Repository URL:'),
23
+ placeholder: 'https://github.com/user/repo.git',
24
+ });
25
+
26
+ if (clack.isCancel(url)) {
27
+ clack.cancel(chalk.yellow('Cancelled'));
28
+ return;
29
+ }
30
+ }
31
+
32
+ if (!directory) {
33
+ if (process.stdin.isTTY) {
34
+ directory = await clack.text({
35
+ message: chalk.cyan('Directory name (optional):'),
36
+ placeholder: 'repo-name',
37
+ required: false,
38
+ });
39
+
40
+ if (clack.isCancel(directory)) {
41
+ clack.cancel(chalk.yellow('Cancelled'));
42
+ return;
43
+ }
44
+ }
45
+ }
46
+
47
+ const depth = args.find((arg) => arg.startsWith('--depth='))?.split('=')[1];
48
+ const branch =
49
+ args.find((arg) => arg.startsWith('--branch='))?.split('=')[1] ||
50
+ args.find((arg) => arg.startsWith('-b='))?.split('=')[1];
51
+
52
+ const spinner = clack.spinner();
53
+ spinner.start(`Cloning ${url}${directory ? ` into ${directory}` : ''}`);
54
+
55
+ let command = `clone ${url}`;
56
+ if (directory) {
57
+ command += ` ${directory}`;
58
+ }
59
+ if (depth) {
60
+ command += ` --depth ${depth}`;
61
+ }
62
+ if (branch) {
63
+ command += ` --branch ${branch}`;
64
+ }
65
+
66
+ const result = execGit(command, { silent: false });
67
+ spinner.stop();
68
+
69
+ if (result.success) {
70
+ clack.outro(chalk.green.bold('Repository cloned'));
71
+ } else {
72
+ clack.cancel(chalk.red('Failed to clone repository'));
73
+ console.error(result.error);
74
+ process.exit(1);
75
+ }
76
+ };
@@ -0,0 +1,82 @@
1
+ const clack = require('@clack/prompts');
2
+ const chalk = require('chalk');
3
+ const { promptQuestions } = require('../lib/commit/questions');
4
+ const buildCommit = require('../lib/commit/build-commit');
5
+ const readConfigFile = require('../lib/config/read-config-file');
6
+
7
+ const { showBanner } = require('../lib/ui/banner');
8
+
9
+ module.exports = async (_args) => {
10
+ showBanner('COMMIT');
11
+ console.log(`${chalk.gray('├')} ${chalk.cyan.bold('Create Commit')}`);
12
+
13
+ // Check if TTY is available for interactive prompts
14
+ if (!process.stdin.isTTY) {
15
+ clack.cancel(chalk.red('Interactive mode required'));
16
+ console.log(chalk.yellow('This command requires interactive input.'));
17
+ console.log(chalk.gray('Please use: git commit -m "message" for non-interactive commits'));
18
+ process.exit(1);
19
+ }
20
+
21
+ const config = readConfigFile();
22
+ if (!config) {
23
+ clack.cancel(chalk.red('No configuration found'));
24
+ process.exit(1);
25
+ }
26
+
27
+ config.subjectLimit = config.subjectLimit || 100;
28
+
29
+ try {
30
+ const answers = await promptQuestions(config);
31
+ const message = buildCommit(answers, config);
32
+
33
+ clack.note(message, chalk.bold('Commit Preview'));
34
+
35
+ const action = await clack.select({
36
+ message: chalk.yellow('Proceed?'),
37
+ options: [
38
+ { value: 'yes', label: chalk.green('Commit') },
39
+ { value: 'no', label: chalk.red('Cancel') },
40
+ ],
41
+ });
42
+
43
+ if (clack.isCancel(action) || action === 'no') {
44
+ clack.cancel(chalk.yellow('Cancelled'));
45
+ return;
46
+ }
47
+
48
+ const { execSync } = require('node:child_process');
49
+
50
+ const spinner = clack.spinner();
51
+ spinner.start('Creating commit...');
52
+
53
+ let result;
54
+ try {
55
+ execSync('git commit -F -', {
56
+ input: message,
57
+ encoding: 'utf8',
58
+ stdio: ['pipe', 'pipe', 'pipe'],
59
+ });
60
+ result = { success: true };
61
+ } catch (error) {
62
+ result = {
63
+ success: false,
64
+ error: error.stderr?.toString() || error.message,
65
+ };
66
+ }
67
+
68
+ spinner.stop();
69
+
70
+ if (result.success) {
71
+ clack.outro(chalk.green.bold('Commit created successfully'));
72
+ } else {
73
+ clack.cancel(chalk.red('Failed to create commit'));
74
+ console.error(result.error);
75
+ process.exit(1);
76
+ }
77
+ } catch (error) {
78
+ clack.cancel(chalk.red('Failed to create commit'));
79
+ console.error(error);
80
+ process.exit(1);
81
+ }
82
+ };