@harryisfish/gitt 1.5.0 → 1.6.2

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.
@@ -6,7 +6,6 @@ const listr2_1 = require("listr2");
6
6
  const prompts_1 = require("@inquirer/prompts");
7
7
  const minimatch_1 = require("minimatch");
8
8
  const errors_1 = require("../errors");
9
- const errors_2 = require("../errors");
10
9
  const git_1 = require("../utils/git");
11
10
  const config_1 = require("../utils/config");
12
11
  const git = (0, simple_git_1.simpleGit)();
@@ -18,6 +17,7 @@ async function cleanDeletedBranches(options = {}) {
18
17
  try {
19
18
  const state = {
20
19
  mainBranch: '',
20
+ currentBranch: '',
21
21
  deletedBranches: []
22
22
  };
23
23
  // Phase 1: Discovery
@@ -27,36 +27,11 @@ async function cleanDeletedBranches(options = {}) {
27
27
  task: async (ctx) => {
28
28
  const mainBranch = await (0, git_1.getMainBranch)();
29
29
  ctx.mainBranch = mainBranch;
30
+ const branchInfo = await git.branchLocal();
31
+ ctx.currentBranch = branchInfo.current;
30
32
  await git.fetch(['origin', mainBranch]);
31
33
  }
32
34
  },
33
- {
34
- title: 'Switch to main branch',
35
- task: async (ctx) => {
36
- // Check if we are on a branch that will be deleted?
37
- // For now, just try to switch to main to be safe.
38
- // But if main is checked out in another worktree, this might fail?
39
- // Let's just try.
40
- try {
41
- await git.checkout(ctx.mainBranch);
42
- }
43
- catch (e) {
44
- // If we can't checkout main (e.g. dirty state), warn but continue?
45
- // Ideally we should be on main to delete other branches safely.
46
- }
47
- }
48
- },
49
- {
50
- title: 'Sync main with remote',
51
- task: async () => {
52
- try {
53
- await git.pull();
54
- }
55
- catch (e) {
56
- // Ignore pull errors (e.g. if not on branch)
57
- }
58
- }
59
- },
60
35
  {
61
36
  title: 'Analyze branches',
62
37
  task: async (ctx) => {
@@ -110,7 +85,7 @@ async function cleanDeletedBranches(options = {}) {
110
85
  await discoveryTasks.run(state);
111
86
  // Phase 2: Interaction / Filtering
112
87
  if (state.deletedBranches.length === 0) {
113
- (0, errors_2.printSuccess)('No branches need to be cleaned up');
88
+ (0, errors_1.printSuccess)('No branches need to be cleaned up');
114
89
  return;
115
90
  }
116
91
  if (options.interactive) {
@@ -128,20 +103,20 @@ async function cleanDeletedBranches(options = {}) {
128
103
  }
129
104
  catch (e) {
130
105
  // User cancelled
131
- throw new Error('Operation cancelled');
106
+ throw new errors_1.UserCancelError('Operation cancelled');
132
107
  }
133
108
  }
134
109
  else {
135
110
  // Auto mode: Filter out unmerged branches
136
111
  const unmerged = state.deletedBranches.filter(b => !b.isMerged);
137
- if (unmerged.length > 0) {
138
- console.log('\nSkipping unmerged branches (use -i to force delete):');
139
- unmerged.forEach(b => console.log(` - ${b.name} (${b.reason})`));
140
- }
141
112
  state.deletedBranches = state.deletedBranches.filter(b => b.isMerged);
113
+ if (state.deletedBranches.length === 0 && unmerged.length > 0) {
114
+ (0, errors_1.printSuccess)(`No merged branches to clean up (${unmerged.length} unmerged branch${unmerged.length > 1 ? 'es' : ''} skipped, use -i to review)`);
115
+ return;
116
+ }
142
117
  }
143
118
  if (state.deletedBranches.length === 0) {
144
- (0, errors_2.printSuccess)('No branches selected for deletion');
119
+ (0, errors_1.printSuccess)('No branches selected for deletion');
145
120
  return;
146
121
  }
147
122
  if (options.dryRun) {
@@ -149,11 +124,23 @@ async function cleanDeletedBranches(options = {}) {
149
124
  state.deletedBranches.forEach(b => console.log(` - ${b.name} (${b.reason})`));
150
125
  return;
151
126
  }
127
+ // Switch to main branch if current branch will be deleted
128
+ const willDeleteCurrent = state.deletedBranches.some(b => b.name === state.currentBranch);
129
+ if (willDeleteCurrent) {
130
+ try {
131
+ await git.checkout(state.mainBranch);
132
+ await git.pull();
133
+ console.log(`Switched to ${state.mainBranch} and synced with remote`);
134
+ }
135
+ catch (e) {
136
+ throw new errors_1.GitError(`Cannot switch to ${state.mainBranch}. Current branch "${state.currentBranch}" will be deleted but checkout failed: ${e instanceof Error ? e.message : 'Unknown error'}`);
137
+ }
138
+ }
152
139
  // Phase 3: Execution
153
140
  const deleteTasks = new listr2_1.Listr([
154
141
  {
155
142
  title: 'Delete branches',
156
- task: (ctx) => {
143
+ task: (_ctx) => {
157
144
  return new listr2_1.Listr(state.deletedBranches.map(branch => ({
158
145
  title: `Delete ${branch.name}`,
159
146
  task: async () => {
@@ -165,9 +152,14 @@ async function cleanDeletedBranches(options = {}) {
165
152
  }
166
153
  ]);
167
154
  await deleteTasks.run(state);
168
- (0, errors_2.printSuccess)('Branch cleanup completed');
155
+ (0, errors_1.printSuccess)('Branch cleanup completed');
169
156
  }
170
157
  catch (error) {
171
- throw new errors_1.GitError(error instanceof Error ? error.message : 'Unknown error occurred while cleaning branches');
158
+ if (error instanceof errors_1.GitError || error instanceof errors_1.UserCancelError) {
159
+ // Re-throw our custom errors as-is
160
+ throw error;
161
+ }
162
+ // Wrap other errors and preserve the original error
163
+ throw new errors_1.GitError(error instanceof Error ? error.message : 'Unknown error occurred while cleaning branches', error instanceof Error ? error : undefined);
172
164
  }
173
165
  }
@@ -0,0 +1,265 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.upgradeCommand = upgradeCommand;
40
+ const prompts_1 = require("@inquirer/prompts");
41
+ const child_process_1 = require("child_process");
42
+ const chalk_1 = __importDefault(require("chalk"));
43
+ const errors_1 = require("../errors");
44
+ const packageJson = require('../../package.json');
45
+ /**
46
+ * Detect which package manager is being used
47
+ */
48
+ function detectPackageManager() {
49
+ try {
50
+ // Check if installed globally with pnpm
51
+ try {
52
+ const result = (0, child_process_1.spawnSync)('pnpm', ['list', '-g', '--depth=0'], {
53
+ encoding: 'utf8',
54
+ stdio: 'pipe'
55
+ });
56
+ if (result.status === 0 && result.stdout.includes(packageJson.name)) {
57
+ return 'pnpm';
58
+ }
59
+ }
60
+ catch (e) {
61
+ // Not installed with pnpm or pnpm not available
62
+ }
63
+ // Check if installed globally with yarn
64
+ try {
65
+ const result = (0, child_process_1.spawnSync)('yarn', ['global', 'list'], {
66
+ encoding: 'utf8',
67
+ stdio: 'pipe'
68
+ });
69
+ if (result.status === 0 && result.stdout.includes(packageJson.name)) {
70
+ return 'yarn';
71
+ }
72
+ }
73
+ catch (e) {
74
+ // Not installed with yarn or yarn not available
75
+ }
76
+ // Check if installed globally with npm
77
+ try {
78
+ const result = (0, child_process_1.spawnSync)('npm', ['list', '-g', '--depth=0'], {
79
+ encoding: 'utf8',
80
+ stdio: 'pipe'
81
+ });
82
+ if (result.status === 0 && result.stdout.includes(packageJson.name)) {
83
+ return 'npm';
84
+ }
85
+ }
86
+ catch (e) {
87
+ // Not installed with npm
88
+ }
89
+ return null;
90
+ }
91
+ catch (error) {
92
+ return null;
93
+ }
94
+ }
95
+ /**
96
+ * Fetch latest release information from GitHub
97
+ */
98
+ async function fetchLatestRelease() {
99
+ try {
100
+ const response = await fetch('https://api.github.com/repos/harryisfish/gitt/releases/latest', {
101
+ headers: {
102
+ 'Accept': 'application/vnd.github.v3+json',
103
+ 'User-Agent': 'gitt-cli'
104
+ }
105
+ });
106
+ if (!response.ok) {
107
+ return null;
108
+ }
109
+ const data = await response.json();
110
+ return {
111
+ version: data.tag_name.replace(/^v/, ''), // Remove 'v' prefix if present
112
+ changelog: data.body || 'No changelog available',
113
+ publishedAt: data.published_at
114
+ };
115
+ }
116
+ catch (error) {
117
+ return null;
118
+ }
119
+ }
120
+ /**
121
+ * Format changelog for display
122
+ */
123
+ function formatChangelog(changelog) {
124
+ // Split by lines and format
125
+ const lines = changelog.split('\n');
126
+ const formatted = [];
127
+ for (const line of lines) {
128
+ if (line.trim() === '')
129
+ continue;
130
+ // Format headers
131
+ if (line.startsWith('## ')) {
132
+ formatted.push(chalk_1.default.bold.cyan(line));
133
+ }
134
+ else if (line.startsWith('### ')) {
135
+ formatted.push(chalk_1.default.bold.yellow(line));
136
+ }
137
+ else if (line.startsWith('- ') || line.startsWith('* ')) {
138
+ formatted.push(chalk_1.default.gray(' ' + line));
139
+ }
140
+ else {
141
+ formatted.push(line);
142
+ }
143
+ }
144
+ return formatted.join('\n');
145
+ }
146
+ /**
147
+ * Check for updates using update-notifier
148
+ */
149
+ async function checkForUpdate() {
150
+ try {
151
+ const updateNotifier = await Promise.resolve().then(() => __importStar(require('update-notifier'))).then(m => m.default);
152
+ const notifier = updateNotifier({
153
+ pkg: packageJson,
154
+ updateCheckInterval: 0 // Force check
155
+ });
156
+ if (notifier.update) {
157
+ return {
158
+ current: notifier.update.current,
159
+ latest: notifier.update.latest,
160
+ type: notifier.update.type
161
+ };
162
+ }
163
+ return null;
164
+ }
165
+ catch (error) {
166
+ return null;
167
+ }
168
+ }
169
+ /**
170
+ * Execute upgrade command
171
+ */
172
+ function executeUpgrade(packageManager, packageName) {
173
+ console.log(chalk_1.default.cyan('\nUpgrading...'));
174
+ try {
175
+ let command;
176
+ let args;
177
+ switch (packageManager) {
178
+ case 'pnpm':
179
+ command = 'pnpm';
180
+ args = ['add', '-g', `${packageName}@latest`];
181
+ break;
182
+ case 'yarn':
183
+ command = 'yarn';
184
+ args = ['global', 'add', `${packageName}@latest`];
185
+ break;
186
+ case 'npm':
187
+ default:
188
+ command = 'npm';
189
+ args = ['install', '-g', `${packageName}@latest`];
190
+ break;
191
+ }
192
+ console.log(chalk_1.default.gray(`Running: ${command} ${args.join(' ')}\n`));
193
+ const result = (0, child_process_1.spawnSync)(command, args, {
194
+ stdio: 'inherit',
195
+ encoding: 'utf8'
196
+ });
197
+ if (result.status !== 0) {
198
+ throw new Error(`Command exited with status ${result.status}`);
199
+ }
200
+ console.log('');
201
+ (0, errors_1.printSuccess)('Upgrade completed successfully!');
202
+ console.log(chalk_1.default.gray('Run "gitt --version" to verify the new version.\n'));
203
+ }
204
+ catch (error) {
205
+ throw new errors_1.GitError('Upgrade failed. Please try upgrading manually:\n' +
206
+ ` ${packageManager} install -g ${packageName}@latest`, error instanceof Error ? error : undefined);
207
+ }
208
+ }
209
+ /**
210
+ * Main upgrade command handler
211
+ */
212
+ async function upgradeCommand() {
213
+ try {
214
+ console.log(chalk_1.default.cyan('🔍 Checking for updates...\n'));
215
+ // Check for updates
216
+ const updateInfo = await checkForUpdate();
217
+ if (!updateInfo) {
218
+ (0, errors_1.printSuccess)('You are already on the latest version! 🎉');
219
+ console.log(chalk_1.default.gray(`Current version: ${packageJson.version}\n`));
220
+ return;
221
+ }
222
+ const { current, latest, type } = updateInfo;
223
+ // Display version comparison
224
+ console.log(chalk_1.default.bold('Version Information:'));
225
+ console.log(` Current: ${chalk_1.default.yellow(current)}`);
226
+ console.log(` Latest: ${chalk_1.default.green(latest)}`);
227
+ console.log(` Type: ${chalk_1.default.gray(type)}\n`);
228
+ // Fetch and display changelog
229
+ console.log(chalk_1.default.bold('📝 Fetching release notes...\n'));
230
+ const releaseInfo = await fetchLatestRelease();
231
+ if (releaseInfo && releaseInfo.changelog) {
232
+ console.log(chalk_1.default.bold('What\'s New:\n'));
233
+ console.log(formatChangelog(releaseInfo.changelog));
234
+ console.log('');
235
+ }
236
+ // Detect package manager
237
+ const packageManager = detectPackageManager();
238
+ if (!packageManager) {
239
+ console.log(chalk_1.default.yellow('⚠ Could not detect package manager.'));
240
+ console.log(chalk_1.default.gray('Please upgrade manually using one of these commands:\n'));
241
+ console.log(chalk_1.default.gray(` npm install -g ${packageJson.name}@latest`));
242
+ console.log(chalk_1.default.gray(` pnpm add -g ${packageJson.name}@latest`));
243
+ console.log(chalk_1.default.gray(` yarn global add ${packageJson.name}@latest\n`));
244
+ return;
245
+ }
246
+ console.log(chalk_1.default.gray(`Package manager detected: ${packageManager}\n`));
247
+ // Ask for confirmation
248
+ const shouldUpgrade = await (0, prompts_1.confirm)({
249
+ message: `Would you like to upgrade to v${latest}?`,
250
+ default: true
251
+ });
252
+ if (!shouldUpgrade) {
253
+ console.log(chalk_1.default.gray('\nUpgrade cancelled.\n'));
254
+ return;
255
+ }
256
+ // Execute upgrade
257
+ executeUpgrade(packageManager, packageJson.name);
258
+ }
259
+ catch (error) {
260
+ if (error instanceof errors_1.GitError) {
261
+ throw error;
262
+ }
263
+ throw new errors_1.GitError('Failed to check for updates. Please check your internet connection.', error instanceof Error ? error : undefined);
264
+ }
265
+ }
package/dist/errors.js CHANGED
@@ -1,14 +1,23 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.UserCancelError = exports.GitError = void 0;
4
7
  exports.handleError = handleError;
5
8
  exports.printSuccess = printSuccess;
6
9
  exports.printError = printError;
7
- // 自定义错误类型
10
+ const chalk_1 = __importDefault(require("chalk"));
11
+ // Custom error types
8
12
  class GitError extends Error {
9
- constructor(message) {
13
+ constructor(message, cause) {
10
14
  super(message);
11
15
  this.name = 'GitError';
16
+ this.cause = cause;
17
+ // Preserve stack trace from the cause if available
18
+ if (cause === null || cause === void 0 ? void 0 : cause.stack) {
19
+ this.stack = `${this.stack}\nCaused by: ${cause.stack}`;
20
+ }
12
21
  }
13
22
  }
14
23
  exports.GitError = GitError;
@@ -19,32 +28,36 @@ class UserCancelError extends Error {
19
28
  }
20
29
  }
21
30
  exports.UserCancelError = UserCancelError;
22
- // 错误消息颜色
23
- const ERROR_COLOR = '\x1b[31m';
24
- const SUCCESS_COLOR = '\x1b[32m';
25
- const RESET_COLOR = '\x1b[0m';
26
- // 统一错误处理函数
31
+ // Unified error handling function
27
32
  function handleError(error) {
28
33
  if (error instanceof UserCancelError) {
29
34
  console.log(error.message);
30
35
  process.exit(0);
31
36
  }
32
37
  if (error instanceof GitError) {
33
- console.error(`${ERROR_COLOR}Error: ${RESET_COLOR}${error.message}`);
38
+ console.error(chalk_1.default.red('Error:'), error.message);
39
+ // In development mode, show full stack trace
40
+ if (process.env.DEBUG || process.env.VERBOSE) {
41
+ console.error(chalk_1.default.gray(error.stack));
42
+ }
34
43
  process.exit(1);
35
44
  }
36
45
  if (error instanceof Error) {
37
- console.error(`${ERROR_COLOR}Program error: ${RESET_COLOR}${error.message}`);
46
+ console.error(chalk_1.default.red('Program error:'), error.message);
47
+ // In development mode, show full stack trace
48
+ if (process.env.DEBUG || process.env.VERBOSE) {
49
+ console.error(chalk_1.default.gray(error.stack));
50
+ }
38
51
  process.exit(1);
39
52
  }
40
- console.error(`${ERROR_COLOR}Unknown error occurred${RESET_COLOR}`);
53
+ console.error(chalk_1.default.red('Unknown error occurred'));
41
54
  process.exit(1);
42
55
  }
43
- // 成功消息处理
56
+ // Success message handler
44
57
  function printSuccess(message) {
45
- console.log(`${SUCCESS_COLOR}${message}${RESET_COLOR}`);
58
+ console.log(chalk_1.default.green(''), message);
46
59
  }
47
- // 错误消息处理
60
+ // Error message handler
48
61
  function printError(message) {
49
- console.error(`${ERROR_COLOR}${message}${RESET_COLOR}`);
62
+ console.error(chalk_1.default.red(message));
50
63
  }
package/dist/index.js CHANGED
@@ -34,39 +34,31 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  };
35
35
  })();
36
36
  Object.defineProperty(exports, "__esModule", { value: true });
37
+ const commander_1 = require("commander");
37
38
  const simple_git_1 = require("simple-git");
38
39
  const errors_1 = require("./errors");
39
40
  const clean_1 = require("./commands/clean");
40
41
  const git = (0, simple_git_1.simpleGit)();
41
- // 处理 Ctrl+C 和其他终止信号
42
+ const packageJson = require('../package.json');
43
+ // Handle Ctrl+C and other termination signals
42
44
  process.on('SIGINT', () => {
43
45
  throw new errors_1.UserCancelError('\nOperation cancelled');
44
46
  });
45
47
  process.on('SIGTERM', () => {
46
48
  throw new errors_1.UserCancelError('\nProgram terminated');
47
49
  });
48
- // 初始化 Git 仓库
49
- async function initGitRepo() {
50
- try {
51
- await git.init();
52
- (0, errors_1.printSuccess)('Git repository initialized successfully');
53
- }
54
- catch (error) {
55
- throw new errors_1.GitError('Failed to initialize Git repository');
56
- }
57
- }
58
- // 检查当前目录是否是 Git 仓库
50
+ // Check if current directory is a Git repository
59
51
  async function checkGitRepo() {
60
52
  const isRepo = await git.checkIsRepo();
61
53
  if (!isRepo) {
62
54
  throw new errors_1.GitError('Current directory is not a Git repository');
63
55
  }
64
- // 检查是否有远程仓库配置
56
+ // Check if remote repository is configured
65
57
  const remotes = await git.getRemotes();
66
58
  if (remotes.length === 0) {
67
59
  throw new errors_1.GitError('Current Git repository has no remote configured');
68
60
  }
69
- // 检查是否能访问远程仓库
61
+ // Check if remote repository is accessible
70
62
  try {
71
63
  await git.fetch(['--dry-run']);
72
64
  }
@@ -74,104 +66,79 @@ async function checkGitRepo() {
74
66
  throw new errors_1.GitError('Cannot access remote repository, please check network connection or repository permissions');
75
67
  }
76
68
  }
77
- function printHelp() {
78
- console.log(`
79
- Usage: gitt [command] [options]
80
-
81
- Commands:
82
- (default) Clean up local branches that have been deleted on remote
83
- set-main <branch> Set the main branch for the current project
84
- ignore <pattern> Add a branch pattern to the ignore list (e.g., "release/*")
85
-
86
- Options:
87
- -i, --interactive Interactive mode: Select branches to delete
88
- -d, --dry-run Dry run: Show what would be deleted without deleting
89
- --stale [days] Find stale branches (default: 90 days, use with a number for custom)
90
- -v, --version Show version number
91
- -h, --help Show this help message
92
-
93
- Examples:
94
- gitt # Auto-clean deleted branches
95
- gitt -i # Select branches to delete interactively
96
- gitt -d # Preview deletion
97
- gitt --stale # Find branches inactive for 90+ days
98
- gitt --stale 30 # Find branches inactive for 30+ days
99
- gitt ignore "temp/*" # Ignore branches matching "temp/*"
100
- gitt set-main master # Set main branch to 'master'
101
- `);
102
- }
103
69
  async function main() {
104
70
  try {
105
- const args = process.argv.slice(2);
106
- const command = args[0];
107
- // Check for version command
108
- if (args.includes('-v') || args.includes('--version')) {
109
- const packageJson = require('../package.json');
110
- console.log(`v${packageJson.version}`);
111
- process.exit(0);
112
- }
113
- // Check for help command
114
- if (args.includes('-h') || args.includes('--help')) {
115
- printHelp();
116
- process.exit(0);
117
- }
118
- // Check for updates
71
+ // Check for updates before parsing commands
119
72
  try {
120
- // Use eval to prevent TypeScript from transpiling dynamic import to require()
121
- const { default: updateNotifier } = await eval('import("update-notifier")');
122
- const pkg = require('../package.json');
123
- updateNotifier({ pkg }).notify();
73
+ // Use dynamic import to load update-notifier
74
+ const updateNotifier = await Promise.resolve().then(() => __importStar(require('update-notifier'))).then(m => m.default);
75
+ updateNotifier({ pkg: packageJson }).notify();
124
76
  }
125
77
  catch (e) {
126
78
  // Ignore update check errors
127
79
  }
128
- // 检查 Git 仓库
129
- await checkGitRepo();
130
- if (command === 'set-main') {
131
- const branch = args[1];
132
- if (!branch) {
133
- throw new Error('Please specify a branch name');
134
- }
135
- await Promise.resolve().then(() => __importStar(require('./commands/config'))).then(m => m.configMainBranch(branch));
136
- }
137
- else if (command === 'ignore') {
138
- const pattern = args[1];
139
- if (!pattern) {
140
- throw new Error('Please specify a branch pattern to ignore');
141
- }
142
- await Promise.resolve().then(() => __importStar(require('./commands/config'))).then(m => m.configIgnoreBranch(pattern));
143
- }
144
- else {
145
- // Parse options
146
- const isInteractive = args.includes('-i') || args.includes('--interactive');
147
- const isDryRun = args.includes('-d') || args.includes('--dry-run');
148
- // Parse stale options
149
- const staleIndex = args.indexOf('--stale');
150
- const isStale = staleIndex !== -1;
151
- let staleDays = 90; // Default 3 months
152
- if (isStale) {
153
- // Check if next arg is a number (days)
154
- // Note: This is a simple check, ideally use a proper arg parser like commander or yargs
155
- const nextArg = args[staleIndex + 1];
156
- if (nextArg && !nextArg.startsWith('-') && !isNaN(Number(nextArg))) {
157
- staleDays = Number(nextArg);
158
- }
159
- }
160
- // 默认执行清理操作
80
+ const program = new commander_1.Command();
81
+ program
82
+ .name('gitt')
83
+ .description('A CLI tool for Git branch management')
84
+ .version(packageJson.version, '-v, --version', 'Show version number');
85
+ // Default command: clean deleted branches
86
+ program
87
+ .option('-i, --interactive', 'Interactive mode: Select branches to delete')
88
+ .option('-d, --dry-run', 'Dry run: Show what would be deleted without deleting')
89
+ .option('--stale [days]', 'Find stale branches (default: 90 days)', '90')
90
+ .action(async (options) => {
91
+ await checkGitRepo();
92
+ const staleDays = options.stale === true ? 90 : parseInt(options.stale, 10);
93
+ const isStale = options.stale !== undefined;
161
94
  await (0, clean_1.cleanDeletedBranches)({
162
- interactive: isInteractive,
163
- dryRun: isDryRun,
95
+ interactive: options.interactive || false,
96
+ dryRun: options.dryRun || false,
164
97
  stale: isStale,
165
98
  staleDays: staleDays
166
99
  });
167
- }
168
- // 退出程序
169
- process.exit(0);
100
+ });
101
+ // set-main command
102
+ program
103
+ .command('set-main <branch>')
104
+ .description('Set the main branch for the current project')
105
+ .action(async (branch) => {
106
+ await checkGitRepo();
107
+ await Promise.resolve().then(() => __importStar(require('./commands/config'))).then(m => m.configMainBranch(branch));
108
+ });
109
+ // ignore command
110
+ program
111
+ .command('ignore <pattern>')
112
+ .description('Add a branch pattern to the ignore list (e.g., "release/*")')
113
+ .action(async (pattern) => {
114
+ await checkGitRepo();
115
+ await Promise.resolve().then(() => __importStar(require('./commands/config'))).then(m => m.configIgnoreBranch(pattern));
116
+ });
117
+ // upgrade command
118
+ program
119
+ .command('upgrade')
120
+ .description('Check for updates and upgrade to the latest version')
121
+ .action(async () => {
122
+ // No need to check Git repo for upgrade command
123
+ await Promise.resolve().then(() => __importStar(require('./commands/upgrade'))).then(m => m.upgradeCommand());
124
+ });
125
+ // Add examples to help
126
+ program.addHelpText('after', `
127
+ Examples:
128
+ $ gitt # Auto-clean deleted branches
129
+ $ gitt -i # Select branches to delete interactively
130
+ $ gitt -d # Preview deletion
131
+ $ gitt --stale # Find branches inactive for 90+ days
132
+ $ gitt --stale 30 # Find branches inactive for 30+ days
133
+ $ gitt ignore "temp/*" # Ignore branches matching "temp/*"
134
+ $ gitt set-main master # Set main branch to 'master'
135
+ $ gitt upgrade # Check for updates and upgrade
136
+ `);
137
+ await program.parseAsync(process.argv);
170
138
  }
171
139
  catch (error) {
172
140
  (0, errors_1.handleError)(error);
173
- process.exit(1);
174
141
  }
175
142
  }
176
- // 启动程序
177
- main().catch(errors_1.handleError);
143
+ // Start the program
144
+ main();