@mexty/cli 1.8.2 → 1.9.1

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 (43) hide show
  1. package/dist/block-68f6a9dd608928af6fbea9ba/.eslintrc +14 -0
  2. package/dist/block-68f6a9dd608928af6fbea9ba/.prettierrc +10 -0
  3. package/dist/block-68f6a9dd608928af6fbea9ba/README.md +63 -0
  4. package/dist/block-68f6a9dd608928af6fbea9ba/index.html +13 -0
  5. package/dist/block-68f6a9dd608928af6fbea9ba/package-lock.json +6657 -0
  6. package/dist/block-68f6a9dd608928af6fbea9ba/package.json +34 -0
  7. package/dist/block-68f6a9dd608928af6fbea9ba/src/App.tsx +45 -0
  8. package/dist/block-68f6a9dd608928af6fbea9ba/src/block.tsx +111 -0
  9. package/dist/block-68f6a9dd608928af6fbea9ba/src/index.tsx +6 -0
  10. package/dist/block-68f6a9dd608928af6fbea9ba/src/styles.css +8 -0
  11. package/dist/block-68f6a9dd608928af6fbea9ba/tsconfig.json +27 -0
  12. package/dist/block-68f6a9dd608928af6fbea9ba/vite.config.ts +20 -0
  13. package/dist/block-68f6a9dd608928af6fbea9ba/webpack.config.js +59 -0
  14. package/dist/block-68f6aa43608928af6fbea9c9/.eslintrc +14 -0
  15. package/dist/block-68f6aa43608928af6fbea9c9/.prettierrc +10 -0
  16. package/dist/block-68f6aa43608928af6fbea9c9/README.md +63 -0
  17. package/dist/block-68f6aa43608928af6fbea9c9/coco +0 -0
  18. package/dist/block-68f6aa43608928af6fbea9c9/index.html +13 -0
  19. package/dist/block-68f6aa43608928af6fbea9c9/package-lock.json +6657 -0
  20. package/dist/block-68f6aa43608928af6fbea9c9/package.json +34 -0
  21. package/dist/block-68f6aa43608928af6fbea9c9/src/App.tsx +45 -0
  22. package/dist/block-68f6aa43608928af6fbea9c9/src/block.tsx +111 -0
  23. package/dist/block-68f6aa43608928af6fbea9c9/src/index.tsx +6 -0
  24. package/dist/block-68f6aa43608928af6fbea9c9/src/styles.css +8 -0
  25. package/dist/block-68f6aa43608928af6fbea9c9/tsconfig.json +27 -0
  26. package/dist/block-68f6aa43608928af6fbea9c9/vite.config.ts +20 -0
  27. package/dist/block-68f6aa43608928af6fbea9c9/webpack.config.js +59 -0
  28. package/dist/coco +0 -0
  29. package/dist/commands/create.d.ts.map +1 -1
  30. package/dist/commands/create.js +61 -1
  31. package/dist/commands/create.js.map +1 -1
  32. package/dist/utils/api.d.ts +7 -0
  33. package/dist/utils/api.d.ts.map +1 -1
  34. package/dist/utils/api.js +4 -0
  35. package/dist/utils/api.js.map +1 -1
  36. package/dist/utils/git.d.ts.map +1 -1
  37. package/dist/utils/git.js +55 -9
  38. package/dist/utils/git.js.map +1 -1
  39. package/package.json +1 -1
  40. package/src/commands/create.ts +69 -1
  41. package/src/commands/github-login.ts +107 -107
  42. package/src/utils/api.ts +5 -0
  43. package/src/utils/git.ts +269 -254
package/src/utils/git.ts CHANGED
@@ -1,254 +1,269 @@
1
- import simpleGit, { SimpleGit } from "simple-git";
2
- import path from "path";
3
- import fs from "fs";
4
- import chalk from "chalk";
5
- import { apiClient } from "./api";
6
-
7
- // Simple spinner implementation since ora v5 has import issues
8
- class SimpleSpinner {
9
- private message: string;
10
- private interval: NodeJS.Timeout | null = null;
11
- private frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
12
- private currentFrame = 0;
13
-
14
- constructor(message: string) {
15
- this.message = message;
16
- }
17
-
18
- // Add setter for message text
19
- set text(newMessage: string) {
20
- this.message = newMessage;
21
- }
22
-
23
- start(): this {
24
- process.stdout.write(this.message);
25
- this.interval = setInterval(() => {
26
- process.stdout.write(
27
- `\r${this.frames[this.currentFrame]} ${this.message}`
28
- );
29
- this.currentFrame = (this.currentFrame + 1) % this.frames.length;
30
- }, 80);
31
- return this;
32
- }
33
-
34
- succeed(message: string): void {
35
- this.stop();
36
- console.log(`\r✅ ${message}`);
37
- }
38
-
39
- fail(message: string): void {
40
- this.stop();
41
- console.log(`\r❌ ${message}`);
42
- }
43
-
44
- private stop(): void {
45
- if (this.interval) {
46
- clearInterval(this.interval);
47
- this.interval = null;
48
- }
49
- process.stdout.write("\r");
50
- }
51
- }
52
-
53
- function ora(message: string): SimpleSpinner {
54
- return new SimpleSpinner(message);
55
- }
56
-
57
- export class GitManager {
58
- public git: SimpleGit;
59
-
60
- constructor(cwd?: string) {
61
- this.git = simpleGit(cwd);
62
- }
63
-
64
- /**
65
- * Clone a repository to a local directory
66
- * Supports private repositories if GitHub is connected
67
- */
68
- async cloneRepository(repoUrl: string, targetDir: string): Promise<void> {
69
- const spinner = ora(`Cloning repository from ${repoUrl}...`).start();
70
-
71
- try {
72
- // Ensure target directory doesn't exist
73
- if (fs.existsSync(targetDir)) {
74
- spinner.fail(chalk.red(`Directory ${targetDir} already exists`));
75
- throw new Error(`Directory ${targetDir} already exists`);
76
- }
77
-
78
- // Check if this is a GitHub URL that might need authentication
79
- const isGitHub = repoUrl.includes('github.com');
80
- let authenticatedUrl = repoUrl;
81
-
82
- if (isGitHub) {
83
- try {
84
- // Try to get GitHub token for private repo access
85
- const tokenData = await apiClient.getGitHubToken();
86
-
87
- if (tokenData.success && tokenData.token) {
88
- // Inject token into URL for authenticated cloning
89
- authenticatedUrl = this.injectTokenIntoUrl(repoUrl, tokenData.token);
90
- spinner.text = `Cloning repository (authenticated)...`;
91
- }
92
- } catch (error: any) {
93
- // If token retrieval fails (e.g., not connected), try without auth
94
- // This is fine for public repositories
95
- if (error.response?.status === 404) {
96
- spinner.text = `Cloning repository (public)...`;
97
- }
98
- }
99
- }
100
-
101
- // Clone the repository
102
- await this.git.clone(authenticatedUrl, targetDir);
103
-
104
- spinner.succeed(chalk.green(`Repository cloned to ${targetDir}`));
105
- } catch (error: any) {
106
- // Check if error is due to authentication
107
- if (error.message.includes('Authentication failed') ||
108
- error.message.includes('could not read Username') ||
109
- error.message.includes('Repository not found')) {
110
- spinner.fail(chalk.red(`Failed to clone repository: Authentication required`));
111
- console.log(chalk.yellow('\n💡 This might be a private repository.'));
112
- console.log(chalk.blue(' Connect your GitHub account: mexty github-login\n'));
113
- } else {
114
- spinner.fail(chalk.red(`Failed to clone repository: ${error.message}`));
115
- }
116
- throw error;
117
- }
118
- }
119
-
120
- /**
121
- * Inject GitHub token into repository URL for authenticated access
122
- */
123
- private injectTokenIntoUrl(repoUrl: string, token: string): string {
124
- // Handle HTTPS URLs
125
- if (repoUrl.startsWith('https://github.com/')) {
126
- return repoUrl.replace('https://github.com/', `https://${token}@github.com/`);
127
- }
128
-
129
- // Handle SSH URLs (convert to HTTPS with token)
130
- if (repoUrl.startsWith('git@github.com:')) {
131
- const path = repoUrl.replace('git@github.com:', '');
132
- return `https://${token}@github.com/${path}`;
133
- }
134
-
135
- // Return original URL if format not recognized
136
- return repoUrl;
137
- }
138
-
139
- /**
140
- * Check if current directory is a Git repository
141
- */
142
- async isGitRepository(dir?: string): Promise<boolean> {
143
- try {
144
- const git = dir ? simpleGit(dir) : this.git;
145
- await git.status();
146
- return true;
147
- } catch (error) {
148
- return false;
149
- }
150
- }
151
-
152
- /**
153
- * Get the current repository's remote URL
154
- */
155
- async getRemoteUrl(dir?: string): Promise<string | null> {
156
- try {
157
- const git = dir ? simpleGit(dir) : this.git;
158
- const remotes = await git.getRemotes(true);
159
- const origin = remotes.find((remote) => remote.name === "origin");
160
- return origin?.refs?.fetch || null;
161
- } catch (error) {
162
- return null;
163
- }
164
- }
165
-
166
- /**
167
- * Check if there are uncommitted changes
168
- */
169
- async hasUncommittedChanges(dir?: string): Promise<boolean> {
170
- try {
171
- const git = dir ? simpleGit(dir) : this.git;
172
- const status = await git.status();
173
- return !status.isClean();
174
- } catch (error) {
175
- return false;
176
- }
177
- }
178
-
179
- /**
180
- * Push current branch to remote
181
- */
182
- async pushToRemote(dir?: string): Promise<void> {
183
- const spinner = ora("Pushing changes to remote repository...").start();
184
-
185
- try {
186
- const git = dir ? simpleGit(dir) : this.git;
187
-
188
- // Get current branch
189
- const status = await git.status();
190
- const currentBranch = status.current;
191
-
192
- if (!currentBranch) {
193
- throw new Error("No current branch found");
194
- }
195
-
196
- // Push to remote
197
- await git.push("origin", currentBranch);
198
-
199
- spinner.succeed(chalk.green("Changes pushed to remote repository"));
200
- } catch (error: any) {
201
- spinner.fail(chalk.red(`Failed to push changes: ${error.message}`));
202
- throw error;
203
- }
204
- }
205
-
206
- /**
207
- * Get repository information
208
- */
209
- async getRepositoryInfo(dir?: string): Promise<{
210
- branch: string;
211
- remoteUrl: string | null;
212
- hasChanges: boolean;
213
- }> {
214
- const git = dir ? simpleGit(dir) : this.git;
215
-
216
- const status = await git.status();
217
- const remoteUrl = await this.getRemoteUrl(dir);
218
-
219
- return {
220
- branch: status.current || "unknown",
221
- remoteUrl,
222
- hasChanges: !status.isClean(),
223
- };
224
- }
225
-
226
- /**
227
- * Extract repository name from URL
228
- */
229
- static extractRepoName(gitUrl: string): string {
230
- // Handle both SSH and HTTPS URLs
231
- const match = gitUrl.match(/\/([^\/]+?)(?:\.git)?$/);
232
- if (match) {
233
- return match[1];
234
- }
235
-
236
- // Fallback: use the last part of the URL
237
- const parts = gitUrl.split("/");
238
- return parts[parts.length - 1].replace(".git", "");
239
- }
240
-
241
- /**
242
- * Validate Git URL format
243
- */
244
- static isValidGitUrl(url: string): boolean {
245
- const patterns = [
246
- /^https:\/\/github\.com\/[\w\-\.]+\/[\w\-\.]+(?:\.git)?$/,
247
- /^git@github\.com:[\w\-\.]+\/[\w\-\.]+(?:\.git)?$/,
248
- /^https:\/\/gitlab\.com\/[\w\-\.]+\/[\w\-\.]+(?:\.git)?$/,
249
- /^git@gitlab\.com:[\w\-\.]+\/[\w\-\.]+(?:\.git)?$/,
250
- ];
251
-
252
- return patterns.some((pattern) => pattern.test(url));
253
- }
254
- }
1
+ import simpleGit, { SimpleGit } from "simple-git";
2
+ import path from "path";
3
+ import fs from "fs";
4
+ import chalk from "chalk";
5
+ import { apiClient } from "./api";
6
+
7
+ // Simple spinner implementation since ora v5 has import issues
8
+ class SimpleSpinner {
9
+ private message: string;
10
+ private interval: NodeJS.Timeout | null = null;
11
+ private frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
12
+ private currentFrame = 0;
13
+
14
+ constructor(message: string) {
15
+ this.message = message;
16
+ }
17
+
18
+ // Add setter for message text
19
+ set text(newMessage: string) {
20
+ this.message = newMessage;
21
+ }
22
+
23
+ start(): this {
24
+ process.stdout.write(this.message);
25
+ this.interval = setInterval(() => {
26
+ process.stdout.write(
27
+ `\r${this.frames[this.currentFrame]} ${this.message}`
28
+ );
29
+ this.currentFrame = (this.currentFrame + 1) % this.frames.length;
30
+ }, 80);
31
+ return this;
32
+ }
33
+
34
+ succeed(message: string): void {
35
+ this.stop();
36
+ console.log(`\r✅ ${message}`);
37
+ }
38
+
39
+ fail(message: string): void {
40
+ this.stop();
41
+ console.log(`\r❌ ${message}`);
42
+ }
43
+
44
+ private stop(): void {
45
+ if (this.interval) {
46
+ clearInterval(this.interval);
47
+ this.interval = null;
48
+ }
49
+ process.stdout.write("\r");
50
+ }
51
+ }
52
+
53
+ function ora(message: string): SimpleSpinner {
54
+ return new SimpleSpinner(message);
55
+ }
56
+
57
+ export class GitManager {
58
+ public git: SimpleGit;
59
+
60
+ constructor(cwd?: string) {
61
+ this.git = simpleGit(cwd);
62
+ }
63
+
64
+ /**
65
+ * Clone a repository to a local directory
66
+ * Supports private repositories if GitHub is connected
67
+ */
68
+ async cloneRepository(repoUrl: string, targetDir: string): Promise<void> {
69
+ const spinner = ora(`Cloning repository from ${repoUrl}...`).start();
70
+
71
+ try {
72
+ // Ensure target directory doesn't exist
73
+ if (fs.existsSync(targetDir)) {
74
+ spinner.fail(chalk.red(`Directory ${targetDir} already exists`));
75
+ throw new Error(`Directory ${targetDir} already exists`);
76
+ }
77
+
78
+ // Check if this is a GitHub URL that might need authentication
79
+ const isGitHub = repoUrl.includes('github.com');
80
+ let authenticatedUrl = repoUrl;
81
+
82
+ if (isGitHub) {
83
+ try {
84
+ // Try to get GitHub token for private repo access
85
+ const tokenData = await apiClient.getGitHubToken();
86
+
87
+ if (tokenData.success && tokenData.token) {
88
+ // Inject token into URL for authenticated cloning
89
+ authenticatedUrl = this.injectTokenIntoUrl(repoUrl, tokenData.token);
90
+ spinner.text = `Cloning repository (authenticated)...`;
91
+ }
92
+ } catch (error: any) {
93
+ // If token retrieval fails (e.g., not connected), try without auth
94
+ // This is fine for public repositories
95
+ if (error.response?.status === 404) {
96
+ spinner.text = `Cloning repository (public)...`;
97
+ }
98
+ }
99
+ }
100
+
101
+ // Clone the repository using native git command
102
+ try {
103
+ const { exec } = await import('child_process');
104
+ const { promisify } = await import('util');
105
+ const execAsync = promisify(exec);
106
+
107
+ const cloneCommand = `git clone "${authenticatedUrl}" "${targetDir}"`;
108
+ await execAsync(cloneCommand);
109
+
110
+ spinner.succeed(chalk.green(`Repository cloned to ${targetDir}`));
111
+ } catch (cloneErr: any) {
112
+ throw cloneErr;
113
+ }
114
+ } catch (error: any) {
115
+ // Check if error is due to authentication
116
+ if (error.message.includes('Authentication failed') ||
117
+ error.message.includes('could not read Username') ||
118
+ error.message.includes('Repository not found')) {
119
+ spinner.fail(chalk.red(`Failed to clone repository: Authentication required`));
120
+ console.log(chalk.yellow('\n💡 This might be a private repository.'));
121
+ console.log(chalk.blue(' Connect your GitHub account: mexty github-login\n'));
122
+ } else {
123
+ spinner.fail(chalk.red(`Failed to clone repository: ${error.message}`));
124
+ }
125
+ throw error;
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Inject GitHub token into repository URL for authenticated access
131
+ */
132
+ private injectTokenIntoUrl(repoUrl: string, token: string): string {
133
+ // GitHub recommends using 'x-access-token' as the username with the token as password
134
+ // Format: https://x-access-token:{token}@github.com/...
135
+
136
+ // Remove trailing slash if present (can cause issues with git clone)
137
+ const cleanUrl = repoUrl.replace(/\/$/, '');
138
+
139
+ // Handle HTTPS URLs
140
+ if (cleanUrl.startsWith('https://github.com/')) {
141
+ return cleanUrl.replace('https://github.com/', `https://x-access-token:${token}@github.com/`);
142
+ }
143
+
144
+ // Handle SSH URLs (convert to HTTPS with token)
145
+ if (cleanUrl.startsWith('git@github.com:')) {
146
+ const path = cleanUrl.replace('git@github.com:', '');
147
+ return `https://x-access-token:${token}@github.com/${path}`;
148
+ }
149
+
150
+ // Return original URL if format not recognized
151
+ return cleanUrl;
152
+ }
153
+
154
+ /**
155
+ * Check if current directory is a Git repository
156
+ */
157
+ async isGitRepository(dir?: string): Promise<boolean> {
158
+ try {
159
+ const git = dir ? simpleGit(dir) : this.git;
160
+ await git.status();
161
+ return true;
162
+ } catch (error) {
163
+ return false;
164
+ }
165
+ }
166
+
167
+ /**
168
+ * Get the current repository's remote URL
169
+ */
170
+ async getRemoteUrl(dir?: string): Promise<string | null> {
171
+ try {
172
+ const git = dir ? simpleGit(dir) : this.git;
173
+ const remotes = await git.getRemotes(true);
174
+ const origin = remotes.find((remote) => remote.name === "origin");
175
+ return origin?.refs?.fetch || null;
176
+ } catch (error) {
177
+ return null;
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Check if there are uncommitted changes
183
+ */
184
+ async hasUncommittedChanges(dir?: string): Promise<boolean> {
185
+ try {
186
+ const git = dir ? simpleGit(dir) : this.git;
187
+ const status = await git.status();
188
+ return !status.isClean();
189
+ } catch (error) {
190
+ return false;
191
+ }
192
+ }
193
+
194
+ /**
195
+ * Push current branch to remote
196
+ */
197
+ async pushToRemote(dir?: string): Promise<void> {
198
+ const spinner = ora("Pushing changes to remote repository...").start();
199
+
200
+ try {
201
+ const git = dir ? simpleGit(dir) : this.git;
202
+
203
+ // Get current branch
204
+ const status = await git.status();
205
+ const currentBranch = status.current;
206
+
207
+ if (!currentBranch) {
208
+ throw new Error("No current branch found");
209
+ }
210
+
211
+ // Push to remote
212
+ await git.push("origin", currentBranch);
213
+
214
+ spinner.succeed(chalk.green("Changes pushed to remote repository"));
215
+ } catch (error: any) {
216
+ spinner.fail(chalk.red(`Failed to push changes: ${error.message}`));
217
+ throw error;
218
+ }
219
+ }
220
+
221
+ /**
222
+ * Get repository information
223
+ */
224
+ async getRepositoryInfo(dir?: string): Promise<{
225
+ branch: string;
226
+ remoteUrl: string | null;
227
+ hasChanges: boolean;
228
+ }> {
229
+ const git = dir ? simpleGit(dir) : this.git;
230
+
231
+ const status = await git.status();
232
+ const remoteUrl = await this.getRemoteUrl(dir);
233
+
234
+ return {
235
+ branch: status.current || "unknown",
236
+ remoteUrl,
237
+ hasChanges: !status.isClean(),
238
+ };
239
+ }
240
+
241
+ /**
242
+ * Extract repository name from URL
243
+ */
244
+ static extractRepoName(gitUrl: string): string {
245
+ // Handle both SSH and HTTPS URLs
246
+ const match = gitUrl.match(/\/([^\/]+?)(?:\.git)?$/);
247
+ if (match) {
248
+ return match[1];
249
+ }
250
+
251
+ // Fallback: use the last part of the URL
252
+ const parts = gitUrl.split("/");
253
+ return parts[parts.length - 1].replace(".git", "");
254
+ }
255
+
256
+ /**
257
+ * Validate Git URL format
258
+ */
259
+ static isValidGitUrl(url: string): boolean {
260
+ const patterns = [
261
+ /^https:\/\/github\.com\/[\w\-\.]+\/[\w\-\.]+(?:\.git)?$/,
262
+ /^git@github\.com:[\w\-\.]+\/[\w\-\.]+(?:\.git)?$/,
263
+ /^https:\/\/gitlab\.com\/[\w\-\.]+\/[\w\-\.]+(?:\.git)?$/,
264
+ /^git@gitlab\.com:[\w\-\.]+\/[\w\-\.]+(?:\.git)?$/,
265
+ ];
266
+
267
+ return patterns.some((pattern) => pattern.test(url));
268
+ }
269
+ }