@mexty/cli 1.0.2 → 1.1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mexty/cli",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "description": "MEXT CLI for managing blocks and repositories",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -1,97 +1,165 @@
1
- import chalk from 'chalk';
2
- import path from 'path';
3
- import { apiClient, CreateBlockRequest } from '../utils/api';
4
- import { GitManager } from '../utils/git';
5
- import { createInterface } from 'readline';
6
- import { requireAuthentication, getAuthenticatedUser } from '../utils/auth';
1
+ import chalk from "chalk";
2
+ import path from "path";
3
+ import { apiClient, CreateBlockRequest } from "../utils/api";
4
+ import { GitManager } from "../utils/git";
5
+ import { createInterface } from "readline";
6
+ import { requireAuthentication, getAuthenticatedUser } from "../utils/auth";
7
7
 
8
8
  interface CreateOptions {
9
9
  description?: string;
10
10
  type?: string;
11
+ name?: string;
12
+ category?: string;
11
13
  }
12
14
 
13
15
  // Simple prompt function to replace inquirer
14
- async function prompt(question: string, defaultValue?: string): Promise<string> {
16
+ async function prompt(
17
+ question: string,
18
+ defaultValue?: string
19
+ ): Promise<string> {
15
20
  return new Promise((resolve) => {
16
21
  const rl = createInterface({
17
22
  input: process.stdin,
18
- output: process.stdout
23
+ output: process.stdout,
19
24
  });
20
25
 
21
- const promptText = defaultValue ? `${question} (${defaultValue}): ` : `${question}: `;
22
-
26
+ const promptText = defaultValue
27
+ ? `${question} (${defaultValue}): `
28
+ : `${question}: `;
29
+
23
30
  rl.question(promptText, (answer) => {
24
31
  rl.close();
25
- resolve(answer.trim() || defaultValue || '');
32
+ resolve(answer.trim() || defaultValue || "");
26
33
  });
27
34
  });
28
35
  }
29
36
 
30
- export async function createCommand(name: string, options: CreateOptions): Promise<void> {
37
+ export async function createCommand(
38
+ subcommand?: string,
39
+ options: CreateOptions = {}
40
+ ): Promise<void> {
31
41
  try {
32
42
  // Check authentication first
33
43
  requireAuthentication();
34
-
44
+
35
45
  const user = getAuthenticatedUser();
36
- console.log(chalk.blue(`🚀 Creating new block: ${name}`));
37
- console.log(chalk.gray(` User: ${user?.fullName || user?.email || 'Unknown'}`));
38
46
 
39
- // Get description if not provided
40
- let description = options.description;
41
- if (!description) {
42
- description = await prompt('Enter a description for the block', `Custom block: ${name}`);
47
+ // Handle both old and new syntax
48
+ let blockName: string;
49
+ let blockDescription: string;
50
+ let blockType: string;
51
+
52
+ if (subcommand === "block") {
53
+ // New syntax: mexty create block --name "..." --description "..." --category "..."
54
+ if (!options.name) {
55
+ console.error(
56
+ chalk.red('❌ --name is required when using "mexty create block"')
57
+ );
58
+ console.log(
59
+ chalk.yellow(
60
+ ' Usage: mexty create block --name "Block Name" --description "Description" --category "category"'
61
+ )
62
+ );
63
+ process.exit(1);
64
+ }
65
+
66
+ blockName = options.name;
67
+ blockDescription = options.description || `Custom block: ${blockName}`;
68
+ blockType = options.category || options.type || "custom";
69
+ } else {
70
+ // Old syntax: mexty create "Block Name" --description "..." --type "..."
71
+ if (!subcommand) {
72
+ console.error(chalk.red("❌ Block name is required"));
73
+ console.log(
74
+ chalk.yellow(' Usage: mexty create "Block Name" [options]')
75
+ );
76
+ console.log(
77
+ chalk.yellow(
78
+ ' Or: mexty create block --name "Block Name" [options]'
79
+ )
80
+ );
81
+ process.exit(1);
82
+ }
83
+
84
+ blockName = subcommand;
85
+ blockDescription = options.description || `Custom block: ${blockName}`;
86
+ blockType = options.type || "custom";
43
87
  }
44
88
 
89
+ console.log(chalk.blue(`🚀 Creating new block: ${blockName}`));
90
+ console.log(
91
+ chalk.gray(` User: ${user?.fullName || user?.email || "Unknown"}`)
92
+ );
93
+ console.log(chalk.gray(` Category: ${blockType}`));
94
+
45
95
  // Prepare block data
46
96
  const blockData: CreateBlockRequest = {
47
- blockType: options.type || 'custom',
48
- title: name,
49
- description: description,
50
- allowedBrickTypes: ['text', 'image', 'video', 'code', 'quiz'], // Default allowed types
51
- scope: ['user-store'], // Default scope for CLI-created blocks
52
- content: []
97
+ blockType: blockType,
98
+ title: blockName,
99
+ description: blockDescription,
100
+ allowedBrickTypes: ["text", "image", "video", "code", "quiz"], // Default allowed types
101
+ scope: ["user-store"], // Default scope for CLI-created blocks
102
+ content: [],
53
103
  };
54
104
 
55
- console.log(chalk.yellow('📡 Creating block on server...'));
56
-
105
+ console.log(chalk.yellow("📡 Creating block on server..."));
106
+
57
107
  // Create the block
58
108
  const block = await apiClient.createBlock(blockData);
59
-
109
+
60
110
  console.log(chalk.green(`✅ Block created successfully!`));
61
111
  console.log(chalk.gray(` Block ID: ${block._id}`));
62
112
  console.log(chalk.gray(` Block Type: ${block.blockType}`));
63
-
113
+
64
114
  if (block.gitUrl) {
65
115
  console.log(chalk.gray(` GitHub URL: ${block.gitUrl}`));
66
-
116
+
67
117
  // Clone the repository
68
118
  const repoName = GitManager.extractRepoName(block.gitUrl);
69
119
  const targetDir = path.join(process.cwd(), repoName);
70
-
120
+
71
121
  console.log(chalk.yellow(`📦 Cloning repository to ./${repoName}...`));
72
-
122
+
73
123
  try {
74
124
  const gitManager = new GitManager();
75
125
  await gitManager.cloneRepository(block.gitUrl, targetDir);
76
-
77
- console.log(chalk.green(`🎉 Block created and repository cloned successfully!`));
126
+
127
+ console.log(
128
+ chalk.green(`🎉 Block created and repository cloned successfully!`)
129
+ );
78
130
  console.log(chalk.blue(`\nNext steps:`));
79
131
  console.log(chalk.gray(` 1. cd ${repoName}`));
80
132
  console.log(chalk.gray(` 2. Make your changes`));
81
- console.log(chalk.gray(` 3. git add . && git commit -m "Your changes"`));
82
- console.log(chalk.gray(` 4. mexty publish`));
83
-
133
+ console.log(chalk.gray(` 3. mexty save`));
134
+
135
+ // Change to the cloned directory
136
+ try {
137
+ process.chdir(targetDir);
138
+ console.log(chalk.green(`📁 Changed to directory: ${repoName}`));
139
+ } catch (chdirError: any) {
140
+ console.warn(
141
+ chalk.yellow(
142
+ `⚠️ Could not change to directory: ${chdirError.message}`
143
+ )
144
+ );
145
+ console.log(chalk.gray(` Please manually run: cd ${repoName}`));
146
+ }
84
147
  } catch (cloneError: any) {
85
- console.error(chalk.red(`❌ Failed to clone repository: ${cloneError.message}`));
148
+ console.error(
149
+ chalk.red(`❌ Failed to clone repository: ${cloneError.message}`)
150
+ );
86
151
  console.log(chalk.yellow(`You can manually clone it later:`));
87
152
  console.log(chalk.gray(` git clone ${block.gitUrl}`));
88
153
  }
89
154
  } else {
90
- console.log(chalk.yellow('⚠️ No GitHub repository was created (GitHub not configured)'));
155
+ console.log(
156
+ chalk.yellow(
157
+ "⚠️ No GitHub repository was created (GitHub not configured)"
158
+ )
159
+ );
91
160
  }
92
-
93
161
  } catch (error: any) {
94
162
  console.error(chalk.red(`❌ Failed to create block: ${error.message}`));
95
163
  process.exit(1);
96
164
  }
97
- }
165
+ }
@@ -0,0 +1,213 @@
1
+ import chalk from "chalk";
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import { apiClient } from "../utils/api";
5
+ import { GitManager } from "../utils/git";
6
+ import { createInterface } from "readline";
7
+ import { requireAuthentication, getAuthenticatedUser } from "../utils/auth";
8
+
9
+ // Simple prompt function
10
+ async function prompt(
11
+ question: string,
12
+ defaultValue?: string
13
+ ): Promise<string> {
14
+ return new Promise((resolve) => {
15
+ const rl = createInterface({
16
+ input: process.stdin,
17
+ output: process.stdout,
18
+ });
19
+
20
+ const promptText = defaultValue
21
+ ? `${question} (${defaultValue}): `
22
+ : `${question}: `;
23
+
24
+ rl.question(promptText, (answer) => {
25
+ rl.close();
26
+ resolve(answer.trim() || defaultValue || "");
27
+ });
28
+ });
29
+ }
30
+
31
+ // Extract block ID from package.json or git URL
32
+ async function findBlockId(): Promise<string | null> {
33
+ const packageJsonPath = path.join(process.cwd(), "package.json");
34
+
35
+ if (fs.existsSync(packageJsonPath)) {
36
+ try {
37
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
38
+
39
+ // Look for block ID in package.json name or description
40
+ if (packageJson.name && packageJson.name.startsWith("block-")) {
41
+ return packageJson.name.replace("block-", "");
42
+ }
43
+
44
+ // Look in description for block ID pattern
45
+ if (packageJson.description) {
46
+ const match = packageJson.description.match(
47
+ /block[:\s]+([a-f0-9]{24})/i
48
+ );
49
+ if (match) {
50
+ return match[1];
51
+ }
52
+ }
53
+ } catch (error) {
54
+ console.warn(chalk.yellow("⚠️ Could not parse package.json"));
55
+ }
56
+ }
57
+
58
+ // Try to extract from git remote URL
59
+ try {
60
+ const gitManager = new GitManager();
61
+ const remoteUrl = await gitManager.getRemoteUrl();
62
+
63
+ if (remoteUrl) {
64
+ const match = remoteUrl.match(/block-([a-f0-9]{24})/);
65
+ if (match) {
66
+ return match[1];
67
+ }
68
+ }
69
+ } catch (error) {
70
+ // Ignore git errors
71
+ }
72
+
73
+ return null;
74
+ }
75
+
76
+ export async function saveCommand(): Promise<void> {
77
+ try {
78
+ // Check authentication first
79
+ requireAuthentication();
80
+
81
+ const user = getAuthenticatedUser();
82
+ console.log(chalk.blue("💾 Saving and publishing block..."));
83
+ console.log(
84
+ chalk.gray(` User: ${user?.fullName || user?.email || "Unknown"}`)
85
+ );
86
+
87
+ // Check if we're in a git repository
88
+ const gitManager = new GitManager();
89
+ const isGitRepo = await gitManager.isGitRepository();
90
+
91
+ if (!isGitRepo) {
92
+ console.error(
93
+ chalk.red(
94
+ "❌ Not a git repository. Please run this command from a block repository."
95
+ )
96
+ );
97
+ process.exit(1);
98
+ }
99
+
100
+ // Get repository information
101
+ const repoInfo = await gitManager.getRepositoryInfo();
102
+ console.log(chalk.gray(` Current branch: ${repoInfo.branch}`));
103
+ console.log(chalk.gray(` Remote URL: ${repoInfo.remoteUrl}`));
104
+
105
+ // Find block ID
106
+ const blockId = await findBlockId();
107
+ if (!blockId) {
108
+ console.error(
109
+ chalk.red("❌ Could not determine block ID from repository.")
110
+ );
111
+ console.error(
112
+ chalk.yellow(
113
+ " Make sure you are in a block repository created with mexty"
114
+ )
115
+ );
116
+ process.exit(1);
117
+ }
118
+
119
+ console.log(chalk.gray(` Block ID: ${blockId}`));
120
+
121
+ // Check if there are changes to commit
122
+ if (repoInfo.hasChanges) {
123
+ console.log(
124
+ chalk.yellow("📝 Found uncommitted changes, preparing to commit...")
125
+ );
126
+
127
+ // Get commit message from user
128
+ const commitMessage = await prompt(
129
+ "Enter commit message",
130
+ "Update block content"
131
+ );
132
+
133
+ if (!commitMessage.trim()) {
134
+ console.error(chalk.red("❌ Commit message cannot be empty"));
135
+ process.exit(1);
136
+ }
137
+
138
+ // Stage all changes
139
+ console.log(chalk.yellow("📋 Staging changes (git add .)..."));
140
+ try {
141
+ await gitManager.git.add(".");
142
+ console.log(chalk.green("✅ Changes staged successfully"));
143
+ } catch (addError: any) {
144
+ console.error(
145
+ chalk.red(`❌ Failed to stage changes: ${addError.message}`)
146
+ );
147
+ process.exit(1);
148
+ }
149
+
150
+ // Commit changes
151
+ console.log(chalk.yellow(`💬 Committing changes: "${commitMessage}"...`));
152
+ try {
153
+ await gitManager.git.commit(commitMessage);
154
+ console.log(chalk.green("✅ Changes committed successfully"));
155
+ } catch (commitError: any) {
156
+ console.error(
157
+ chalk.red(`❌ Failed to commit changes: ${commitError.message}`)
158
+ );
159
+ process.exit(1);
160
+ }
161
+ } else {
162
+ console.log(chalk.green("✅ No uncommitted changes found"));
163
+ }
164
+
165
+ // Push changes to remote
166
+ console.log(
167
+ chalk.yellow(`📤 Pushing changes to remote (${repoInfo.branch})...`)
168
+ );
169
+ try {
170
+ await gitManager.pushToRemote();
171
+ console.log(chalk.green("✅ Changes pushed successfully"));
172
+ } catch (pushError: any) {
173
+ console.error(
174
+ chalk.red(`❌ Failed to push changes: ${pushError.message}`)
175
+ );
176
+ console.log(
177
+ chalk.yellow(
178
+ " Please check your network connection and GitHub permissions"
179
+ )
180
+ );
181
+ process.exit(1);
182
+ }
183
+
184
+ // Trigger save and bundle
185
+ console.log(chalk.yellow("🏗️ Triggering build and bundle process..."));
186
+
187
+ try {
188
+ const result = await apiClient.saveAndBundle({ blockId });
189
+
190
+ console.log(chalk.green("🎉 Block saved and published successfully!"));
191
+ console.log(chalk.gray(` Bundle Path: ${result.bundlePath}`));
192
+ console.log(chalk.gray(` Federation URL: ${result.federationUrl}`));
193
+
194
+ if (result.message) {
195
+ console.log(chalk.blue(` ${result.message}`));
196
+ }
197
+
198
+ console.log(
199
+ chalk.blue("\n📋 Your block is now building in the background.")
200
+ );
201
+ console.log(
202
+ chalk.gray(" You can check the build status in the web interface.")
203
+ );
204
+ } catch (buildError: any) {
205
+ console.error(chalk.red(`❌ Build failed: ${buildError.message}`));
206
+ console.log(chalk.yellow(" Check the server logs for more details."));
207
+ process.exit(1);
208
+ }
209
+ } catch (error: any) {
210
+ console.error(chalk.red(`❌ Failed to save block: ${error.message}`));
211
+ process.exit(1);
212
+ }
213
+ }
package/src/index.ts CHANGED
@@ -1,77 +1,91 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { Command } from 'commander';
4
- import chalk from 'chalk';
5
- import { loginCommand } from './commands/login';
6
- import { createCommand } from './commands/create';
7
- import { forkCommand } from './commands/fork';
8
- import { deleteCommand } from './commands/delete';
9
- import { publishCommand } from './commands/publish';
10
- import { syncCommand } from './commands/sync';
11
- import { apiClient } from './utils/api';
3
+ import { Command } from "commander";
4
+ import chalk from "chalk";
5
+ import { loginCommand } from "./commands/login";
6
+ import { createCommand } from "./commands/create";
7
+ import { forkCommand } from "./commands/fork";
8
+ import { deleteCommand } from "./commands/delete";
9
+ import { publishCommand } from "./commands/publish";
10
+ import { syncCommand } from "./commands/sync";
11
+ import { saveCommand } from "./commands/save";
12
+ import { apiClient } from "./utils/api";
12
13
 
13
14
  const program = new Command();
14
15
 
15
16
  // CLI Configuration
16
17
  program
17
- .name('mexty')
18
- .description('MEXT CLI for managing React microfrontend blocks and components')
19
- .version('1.0.0');
18
+ .name("mexty")
19
+ .description(
20
+ "MEXT CLI for managing React microfrontend blocks and components"
21
+ )
22
+ .version("1.0.0");
20
23
 
21
24
  // Add commands
22
25
  program
23
- .command('login')
24
- .description('Authenticate with MEXT')
26
+ .command("login")
27
+ .description("Authenticate with MEXT")
25
28
  .action(loginCommand);
26
29
 
27
30
  program
28
- .command('logout')
29
- .description('Logout from MEXT')
31
+ .command("logout")
32
+ .description("Logout from MEXT")
30
33
  .action(async () => {
31
34
  try {
32
35
  if (!apiClient.isAuthenticated()) {
33
- console.log(chalk.yellow('⚠️ You are not logged in'));
36
+ console.log(chalk.yellow("⚠️ You are not logged in"));
34
37
  return;
35
38
  }
36
-
39
+
37
40
  await apiClient.logout();
38
- console.log(chalk.green('✅ Logged out successfully'));
41
+ console.log(chalk.green("✅ Logged out successfully"));
39
42
  } catch (error: any) {
40
43
  console.error(chalk.red(`❌ Logout failed: ${error.message}`));
41
44
  }
42
45
  });
43
46
 
47
+ // Support both old and new create syntax
44
48
  program
45
- .command('create <name>')
46
- .description('Create a new React microfrontend block')
47
- .option('-d, --description <description>', 'Block description')
48
- .option('-t, --type <type>', 'Block type', 'custom')
49
+ .command("create [subcommand]")
50
+ .description("Create a new React microfrontend block")
51
+ .option("-d, --description <description>", "Block description")
52
+ .option("-t, --type <type>", "Block type", "custom")
53
+ .option("-n, --name <name>", 'Block name (for "create block" syntax)')
54
+ .option(
55
+ "-c, --category <category>",
56
+ 'Block category (for "create block" syntax)'
57
+ )
49
58
  .action(createCommand);
50
59
 
51
60
  program
52
- .command('fork <blockId>')
53
- .description('Fork an existing block and clone its repository')
61
+ .command("fork <blockId>")
62
+ .description("Fork an existing block and clone its repository")
54
63
  .action(forkCommand);
55
64
 
56
65
  program
57
- .command('delete <blockId>')
58
- .description('Delete a block (requires ownership)')
66
+ .command("delete <blockId>")
67
+ .description("Delete a block (requires ownership)")
59
68
  .action(deleteCommand);
60
69
 
61
70
  program
62
- .command('publish')
63
- .description('Publish current block with automatic bundling')
71
+ .command("publish")
72
+ .description("Publish current block with automatic bundling")
64
73
  .action(publishCommand);
65
74
 
66
75
  program
67
- .command('sync')
68
- .description('Sync block registry and update typed exports')
76
+ .command("sync")
77
+ .description("Sync block registry and update typed exports")
69
78
  .action(syncCommand);
70
79
 
80
+ program
81
+ .command("save")
82
+ .description("Save current block (git add, commit, push, and trigger build)")
83
+ .action(saveCommand);
84
+
71
85
  // Error handling
72
- program.on('command:*', () => {
73
- console.error(chalk.red(`Invalid command: ${program.args.join(' ')}`));
74
- console.log(chalk.yellow('See --help for a list of available commands.'));
86
+ program.on("command:*", () => {
87
+ console.error(chalk.red(`Invalid command: ${program.args.join(" ")}`));
88
+ console.log(chalk.yellow("See --help for a list of available commands."));
75
89
  process.exit(1);
76
90
  });
77
91
 
@@ -81,4 +95,4 @@ program.parse(process.argv);
81
95
  // Show help if no command provided
82
96
  if (!process.argv.slice(2).length) {
83
97
  program.outputHelp();
84
- }
98
+ }
package/src/utils/git.ts CHANGED
@@ -1,13 +1,13 @@
1
- import simpleGit, { SimpleGit } from 'simple-git';
2
- import path from 'path';
3
- import fs from 'fs';
4
- import chalk from 'chalk';
1
+ import simpleGit, { SimpleGit } from "simple-git";
2
+ import path from "path";
3
+ import fs from "fs";
4
+ import chalk from "chalk";
5
5
 
6
6
  // Simple spinner implementation since ora v5 has import issues
7
7
  class SimpleSpinner {
8
8
  private message: string;
9
9
  private interval: NodeJS.Timeout | null = null;
10
- private frames = ['', '', '', '', '', '', '', '', '', ''];
10
+ private frames = ["", "", "", "", "", "", "", "", "", ""];
11
11
  private currentFrame = 0;
12
12
 
13
13
  constructor(message: string) {
@@ -17,7 +17,9 @@ class SimpleSpinner {
17
17
  start(): this {
18
18
  process.stdout.write(this.message);
19
19
  this.interval = setInterval(() => {
20
- process.stdout.write(`\r${this.frames[this.currentFrame]} ${this.message}`);
20
+ process.stdout.write(
21
+ `\r${this.frames[this.currentFrame]} ${this.message}`
22
+ );
21
23
  this.currentFrame = (this.currentFrame + 1) % this.frames.length;
22
24
  }, 80);
23
25
  return this;
@@ -38,7 +40,7 @@ class SimpleSpinner {
38
40
  clearInterval(this.interval);
39
41
  this.interval = null;
40
42
  }
41
- process.stdout.write('\r');
43
+ process.stdout.write("\r");
42
44
  }
43
45
  }
44
46
 
@@ -47,7 +49,7 @@ function ora(message: string): SimpleSpinner {
47
49
  }
48
50
 
49
51
  export class GitManager {
50
- private git: SimpleGit;
52
+ public git: SimpleGit;
51
53
 
52
54
  constructor(cwd?: string) {
53
55
  this.git = simpleGit(cwd);
@@ -68,7 +70,7 @@ export class GitManager {
68
70
 
69
71
  // Clone the repository
70
72
  await this.git.clone(repoUrl, targetDir);
71
-
73
+
72
74
  spinner.succeed(chalk.green(`Repository cloned to ${targetDir}`));
73
75
  } catch (error: any) {
74
76
  spinner.fail(chalk.red(`Failed to clone repository: ${error.message}`));
@@ -96,7 +98,7 @@ export class GitManager {
96
98
  try {
97
99
  const git = dir ? simpleGit(dir) : this.git;
98
100
  const remotes = await git.getRemotes(true);
99
- const origin = remotes.find(remote => remote.name === 'origin');
101
+ const origin = remotes.find((remote) => remote.name === "origin");
100
102
  return origin?.refs?.fetch || null;
101
103
  } catch (error) {
102
104
  return null;
@@ -120,23 +122,23 @@ export class GitManager {
120
122
  * Push current branch to remote
121
123
  */
122
124
  async pushToRemote(dir?: string): Promise<void> {
123
- const spinner = ora('Pushing changes to remote repository...').start();
125
+ const spinner = ora("Pushing changes to remote repository...").start();
124
126
 
125
127
  try {
126
128
  const git = dir ? simpleGit(dir) : this.git;
127
-
129
+
128
130
  // Get current branch
129
131
  const status = await git.status();
130
132
  const currentBranch = status.current;
131
133
 
132
134
  if (!currentBranch) {
133
- throw new Error('No current branch found');
135
+ throw new Error("No current branch found");
134
136
  }
135
137
 
136
138
  // Push to remote
137
- await git.push('origin', currentBranch);
138
-
139
- spinner.succeed(chalk.green('Changes pushed to remote repository'));
139
+ await git.push("origin", currentBranch);
140
+
141
+ spinner.succeed(chalk.green("Changes pushed to remote repository"));
140
142
  } catch (error: any) {
141
143
  spinner.fail(chalk.red(`Failed to push changes: ${error.message}`));
142
144
  throw error;
@@ -152,14 +154,14 @@ export class GitManager {
152
154
  hasChanges: boolean;
153
155
  }> {
154
156
  const git = dir ? simpleGit(dir) : this.git;
155
-
157
+
156
158
  const status = await git.status();
157
159
  const remoteUrl = await this.getRemoteUrl(dir);
158
-
160
+
159
161
  return {
160
- branch: status.current || 'unknown',
162
+ branch: status.current || "unknown",
161
163
  remoteUrl,
162
- hasChanges: !status.isClean()
164
+ hasChanges: !status.isClean(),
163
165
  };
164
166
  }
165
167
 
@@ -172,10 +174,10 @@ export class GitManager {
172
174
  if (match) {
173
175
  return match[1];
174
176
  }
175
-
177
+
176
178
  // Fallback: use the last part of the URL
177
- const parts = gitUrl.split('/');
178
- return parts[parts.length - 1].replace('.git', '');
179
+ const parts = gitUrl.split("/");
180
+ return parts[parts.length - 1].replace(".git", "");
179
181
  }
180
182
 
181
183
  /**
@@ -186,9 +188,9 @@ export class GitManager {
186
188
  /^https:\/\/github\.com\/[\w\-\.]+\/[\w\-\.]+(?:\.git)?$/,
187
189
  /^git@github\.com:[\w\-\.]+\/[\w\-\.]+(?:\.git)?$/,
188
190
  /^https:\/\/gitlab\.com\/[\w\-\.]+\/[\w\-\.]+(?:\.git)?$/,
189
- /^git@gitlab\.com:[\w\-\.]+\/[\w\-\.]+(?:\.git)?$/
191
+ /^git@gitlab\.com:[\w\-\.]+\/[\w\-\.]+(?:\.git)?$/,
190
192
  ];
191
193
 
192
- return patterns.some(pattern => pattern.test(url));
194
+ return patterns.some((pattern) => pattern.test(url));
193
195
  }
194
- }
196
+ }