git0 0.1.6 → 0.2.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.
Files changed (3) hide show
  1. package/gg.js +180 -141
  2. package/package.json +7 -5
  3. package/readme.md +11 -14
package/gg.js CHANGED
@@ -3,7 +3,7 @@
3
3
  import inquirer from 'inquirer';
4
4
  import chalk from 'chalk';
5
5
  import { execSync, spawn } from 'child_process';
6
- import axios from 'axios';
6
+ import grab from 'grab-api.js';
7
7
  import * as tar from 'tar'
8
8
  import { pipeline } from 'stream/promises';
9
9
  import fs from 'fs';
@@ -15,24 +15,48 @@ const GITHUB_API = 'https://api.github.com/search/repositories';
15
15
  const RESULTS_PER_PAGE = 10;
16
16
  const TOKEN = process.env.GITHUB_TOKEN;
17
17
 
18
+ const githubHelpUrl = 'https://github.com/settings/personal-access-tokens/new'
19
+ grab('', {
20
+ setDefaults: true,
21
+ debug: false,
22
+ timeout: 5000,
23
+ onError: (error) => {
24
+ if (error.includes('403')) {
25
+ log(chalk.red('Rate limit exceeded. Please set env var GITHUB_TOKEN. Help:\n' + githubHelpUrl));
26
+ process.exit(1);
27
+
28
+ }
29
+ }
30
+ }
31
+ )
32
+
33
+ function printLogo() {
34
+ console.log(chalk.cyan(` ___
35
+ __ _(_)‾|_ / _ \\
36
+ / _ | | __| | | |
37
+ | (_| | | |_| |_| |
38
+ \\__, |_|\\__|\\___/
39
+ |___/`))
40
+ }
41
+
18
42
  // Detect current operating system and architecture
19
43
  function getCurrentPlatform() {
20
44
  const platform = os.platform();
21
45
  const arch = os.arch();
22
-
46
+
23
47
  const platformMap = {
24
48
  'win32': 'windows',
25
49
  'darwin': 'macos',
26
50
  'linux': 'linux'
27
51
  };
28
-
52
+
29
53
  const archMap = {
30
54
  'x64': 'x86_64',
31
55
  'arm64': 'arm64',
32
56
  'arm': 'arm',
33
57
  'ia32': 'i386'
34
58
  };
35
-
59
+
36
60
  return {
37
61
  os: platformMap[platform] || platform,
38
62
  arch: archMap[arch] || arch,
@@ -44,11 +68,13 @@ function getCurrentPlatform() {
44
68
  // Get releases for a repository
45
69
  async function getRepositoryReleases(owner, repo) {
46
70
  try {
47
- const response = await axios.get(`https://api.github.com/repos/${owner}/${repo}/releases`, {
71
+ const response = await grab(`https://api.github.com/repos/${owner}/${repo}/releases`, {
48
72
  headers: TOKEN ? { Authorization: `token ${TOKEN}` } : {},
49
- params: { per_page: 5 } // Get latest 5 releases
73
+ per_page: 5 // Get latest 5 releases
74
+
50
75
  });
51
- return response.data;
76
+
77
+ return response;
52
78
  } catch (error) {
53
79
  return [];
54
80
  }
@@ -61,28 +87,28 @@ function categorizeReleasesByPlatform(releases) {
61
87
  macos: ['mac', 'macos', 'darwin', 'osx', '.dmg', '.pkg'],
62
88
  linux: ['linux', 'ubuntu', 'debian', '.deb', '.rpm', '.tar.gz', '.AppImage']
63
89
  };
64
-
90
+
65
91
  const archKeywords = {
66
92
  x86_64: ['x86_64', 'x64', 'amd64', '64'],
67
93
  arm64: ['arm64', 'aarch64'],
68
94
  arm: ['arm', 'armv7'],
69
95
  i386: ['i386', 'x86', '32']
70
96
  };
71
-
97
+
72
98
  const categorizedReleases = [];
73
-
74
- releases.forEach(release => {
99
+
100
+ Object.entries(releases).forEach(([key, release]) => {
75
101
  const platformAssets = {
76
102
  windows: [],
77
103
  macos: [],
78
104
  linux: [],
79
105
  universal: []
80
106
  };
81
-
107
+
82
108
  release.assets.forEach(asset => {
83
109
  const name = asset.name.toLowerCase();
84
110
  let categorized = false;
85
-
111
+
86
112
  // Check each platform
87
113
  Object.entries(platformKeywords).forEach(([platform, keywords]) => {
88
114
  if (keywords.some(keyword => name.includes(keyword.toLowerCase()))) {
@@ -93,7 +119,7 @@ function categorizeReleasesByPlatform(releases) {
93
119
  detectedArch = arch;
94
120
  }
95
121
  });
96
-
122
+
97
123
  platformAssets[platform].push({
98
124
  ...asset,
99
125
  detectedArch,
@@ -102,10 +128,10 @@ function categorizeReleasesByPlatform(releases) {
102
128
  categorized = true;
103
129
  }
104
130
  });
105
-
131
+
106
132
  // If not categorized, check for universal binaries
107
- if (!categorized && (name.includes('universal') || name.includes('all') ||
108
- (!name.includes('win') && !name.includes('mac') && !name.includes('linux')))) {
133
+ if (!categorized && (name.includes('universal') || name.includes('all') ||
134
+ (!name.includes('win') && !name.includes('mac') && !name.includes('linux')))) {
109
135
  platformAssets.universal.push({
110
136
  ...asset,
111
137
  detectedArch: 'universal',
@@ -113,7 +139,7 @@ function categorizeReleasesByPlatform(releases) {
113
139
  });
114
140
  }
115
141
  });
116
-
142
+
117
143
  // Only include releases that have assets
118
144
  const hasAssets = Object.values(platformAssets).some(assets => assets.length > 0);
119
145
  if (hasAssets) {
@@ -123,52 +149,53 @@ function categorizeReleasesByPlatform(releases) {
123
149
  });
124
150
  }
125
151
  });
126
-
152
+
127
153
  return categorizedReleases;
128
154
  }
129
155
 
130
156
  // Filter releases for current platform (for compatibility indicator)
131
157
  function filterReleasesByPlatform(releases, currentPlatform) {
132
158
  const categorized = categorizeReleasesByPlatform(releases);
133
- return categorized.filter(release =>
159
+ return categorized.filter(release =>
134
160
  release.platformAssets[currentPlatform.os].length > 0 ||
135
161
  release.platformAssets.universal.length > 0
136
162
  );
137
163
  }
138
164
 
165
+
139
166
  // Download and install package
140
167
  async function downloadPackage(asset, targetDir) {
141
168
  const fileName = asset.name;
142
169
  const downloadPath = path.join(targetDir, fileName);
143
-
144
- console.log(chalk.blue(`📦 Downloading ${fileName}...`));
145
-
170
+
171
+ log(chalk.blue(`📦 Downloading ${fileName}...`));
172
+ printLogo()
146
173
  try {
147
- const response = await axios({
148
- url: asset.browser_download_url,
149
- method: 'GET',
150
- responseType: 'stream',
151
- headers: TOKEN ? { Authorization: `token ${TOKEN}` } : {}
174
+ const response = await grab(asset.browser_download_url, {
175
+ headers: TOKEN ? { Authorization: `token ${TOKEN}` } : {},
176
+ onStream: async (res) => {
177
+ const nodeStream = (await import('stream'))?.Readable.fromWeb(res);
178
+ await new Promise((resolve, reject) => {
179
+ nodeStream.pipe(fs.createWriteStream(downloadPath)).on('finish', resolve).on('error', reject);
180
+ });
181
+ }
152
182
  });
153
-
154
- const writer = fs.createWriteStream(downloadPath);
155
- await pipeline(response.data, writer);
156
-
157
- console.log(chalk.green(`✅ Downloaded ${fileName} to ${downloadPath}`));
158
-
183
+
184
+ log(chalk.green(`✅ Downloaded ${fileName} to ${downloadPath}`));
185
+
159
186
  // Try to make executable if it's a binary
160
187
  if (process.platform !== 'win32' && !fileName.includes('.')) {
161
188
  try {
162
189
  fs.chmodSync(downloadPath, '755');
163
- console.log(chalk.green(`✅ Made ${fileName} executable`));
190
+ log(chalk.green(`✅ Made ${fileName} executable`));
164
191
  } catch (error) {
165
- console.log(chalk.yellow(`⚠️ Could not make ${fileName} executable`));
192
+ log(chalk.yellow(`⚠️ Could not make ${fileName} executable`));
166
193
  }
167
194
  }
168
-
195
+
169
196
  // Provide installation instructions
170
197
  provideInstallationInstructions(downloadPath, asset);
171
-
198
+
172
199
  } catch (error) {
173
200
  console.error(chalk.red(`❌ Failed to download ${fileName}:`), error.message);
174
201
  }
@@ -178,40 +205,38 @@ async function downloadPackage(asset, targetDir) {
178
205
  function provideInstallationInstructions(filePath, asset) {
179
206
  const fileName = asset.name;
180
207
  const platform = getCurrentPlatform();
181
-
182
- console.log(chalk.cyan('\n📋 Installation Instructions:'));
183
-
208
+
184
209
  if (platform.platform === 'win32') {
185
210
  if (fileName.endsWith('.exe')) {
186
- console.log(chalk.white(' Run the executable:'));
187
- console.log(chalk.gray(` ${filePath}`));
211
+ log(chalk.white(' Run the executable:'));
212
+ log(chalk.gray(` ${filePath}`));
188
213
  } else if (fileName.endsWith('.msi')) {
189
- console.log(chalk.white(' Install the MSI package:'));
190
- console.log(chalk.gray(` msiexec /i "${filePath}"`));
214
+ log(chalk.white(' Install the MSI package:'));
215
+ log(chalk.gray(` msiexec /i "${filePath}"`));
191
216
  }
192
217
  } else if (platform.platform === 'darwin') {
193
218
  if (fileName.endsWith('.dmg')) {
194
- console.log(chalk.white(' Mount and install the DMG:'));
195
- console.log(chalk.gray(` open "${filePath}"`));
219
+ log(chalk.white(' Mount and install the DMG:'));
220
+ log(chalk.gray(` open "${filePath}"`));
196
221
  } else if (fileName.endsWith('.pkg')) {
197
- console.log(chalk.white(' Install the package:'));
198
- console.log(chalk.gray(` sudo installer -pkg "${filePath}" -target /`));
222
+ log(chalk.white(' Install the package:'));
223
+ log(chalk.gray(` sudo installer -pkg "${filePath}" -target /`));
199
224
  }
200
225
  } else {
201
226
  if (fileName.endsWith('.deb')) {
202
- console.log(chalk.white(' Install the DEB package:'));
203
- console.log(chalk.gray(` sudo dpkg -i "${filePath}"`));
227
+ log(chalk.white(' Install the DEB package:'));
228
+ log(chalk.gray(` sudo dpkg -i "${filePath}"`));
204
229
  } else if (fileName.endsWith('.rpm')) {
205
- console.log(chalk.white(' Install the RPM package:'));
206
- console.log(chalk.gray(` sudo rpm -i "${filePath}"`));
230
+ log(chalk.white(' Install the RPM package:'));
231
+ log(chalk.gray(` sudo rpm -i "${filePath}"`));
207
232
  } else if (fileName.endsWith('.AppImage')) {
208
- console.log(chalk.white(' Run the AppImage:'));
209
- console.log(chalk.gray(` chmod +x "${filePath}" && "${filePath}"`));
233
+ log(chalk.white(' Run the AppImage:'));
234
+ log(chalk.gray(` chmod +x "${filePath}" && "${filePath}"`));
210
235
  } else if (!fileName.includes('.')) {
211
- console.log(chalk.white(' Binary is ready to use:'));
212
- console.log(chalk.gray(` "${filePath}"`));
213
- console.log(chalk.white(' Consider moving to PATH:'));
214
- console.log(chalk.gray(` sudo mv "${filePath}" /usr/local/bin/`));
236
+ log(chalk.white(' Binary is ready to use:'));
237
+ log(chalk.gray(` "${filePath}"`));
238
+ log(chalk.white(' Consider moving to PATH:'));
239
+ log(chalk.gray(` sudo mv "${filePath}" /usr/local/bin/`));
215
240
  }
216
241
  }
217
242
  }
@@ -220,59 +245,60 @@ function provideInstallationInstructions(filePath, asset) {
220
245
  async function showPackageMenu(selectedRepo) {
221
246
  const currentPlatform = getCurrentPlatform();
222
247
  const releaseChoices = [];
223
-
248
+ const limitReleases = 2;
249
+
224
250
  // Add section headers and organize by platform
225
- selectedRepo.allReleases.forEach(release => {
251
+ selectedRepo.allReleases.slice(0, limitReleases).forEach(release => {
226
252
  const platforms = ['windows', 'macos', 'linux', 'universal'];
227
-
253
+
228
254
  platforms.forEach(platform => {
229
255
  const assets = release.platformAssets[platform];
230
256
  if (assets.length > 0) {
231
257
  // Add platform header
232
258
  const platformEmoji = {
233
259
  windows: '🪟',
234
- macos: '🍎',
260
+ macos: '🍎',
235
261
  linux: '🐧',
236
262
  universal: '🌐'
237
263
  };
238
-
264
+
239
265
  const platformName = {
240
266
  windows: 'Windows',
241
267
  macos: 'macOS',
242
268
  linux: 'Linux',
243
269
  universal: 'Universal'
244
270
  };
245
-
271
+
246
272
  const isCurrentPlatform = platform === currentPlatform.os || platform === 'universal';
247
- const platformHeader = isCurrentPlatform
273
+ const platformHeader = isCurrentPlatform
248
274
  ? chalk.green(`${platformEmoji[platform]} ${platformName[platform]} (Your Platform)`)
249
275
  : chalk.gray(`${platformEmoji[platform]} ${platformName[platform]}`);
250
-
276
+
251
277
  // Add separator if not first platform in this release
252
- const needsSeparator = releaseChoices.length > 0 &&
278
+ const needsSeparator = releaseChoices.length > 0 &&
253
279
  !releaseChoices[releaseChoices.length - 1].name.includes('────');
254
-
280
+
255
281
  if (needsSeparator) {
256
282
  releaseChoices.push({
257
283
  name: chalk.gray('────────────────────────────────'),
258
284
  disabled: true
259
285
  });
260
286
  }
261
-
287
+
262
288
  releaseChoices.push({
263
289
  name: `${chalk.bold(release.tag_name)} - ${platformHeader}`,
264
290
  disabled: true
265
291
  });
266
-
292
+
267
293
  // Add assets for this platform
268
294
  assets.forEach(asset => {
269
295
  const sizeStr = (asset.size / 1024 / 1024).toFixed(2);
270
- const archInfo = asset.detectedArch !== 'unknown' && asset.detectedArch !== 'universal'
271
- ? chalk.cyan(`[${asset.detectedArch}]`)
296
+ const archInfo = asset.detectedArch !== 'unknown' && asset.detectedArch !== 'universal'
297
+ ? chalk.cyan(`[${asset.detectedArch}]`)
272
298
  : '';
273
-
299
+
274
300
  const highlight = isCurrentPlatform ? chalk.white : chalk.gray;
275
-
301
+
276
302
  releaseChoices.push({
277
303
  name: ` ${highlight(`${asset.name} ${archInfo} (${sizeStr} MB)`)}`,
278
304
  value: { release, asset }
@@ -283,7 +309,7 @@ async function showPackageMenu(selectedRepo) {
283
309
  });
284
310
 
285
311
  if (releaseChoices.filter(choice => !choice.disabled).length === 0) {
286
- console.log(chalk.yellow('No packages found for download.'));
312
+ log(chalk.yellow('No packages found for download.'));
287
313
  return;
288
314
  }
289
315
 
@@ -295,7 +321,7 @@ async function showPackageMenu(selectedRepo) {
295
321
  pageSize: 15
296
322
  });
297
323
 
298
- const downloadDir = path.resolve(process.cwd(), 'downloads');
324
+ const downloadDir = path.resolve(process.cwd());
299
325
  fs.mkdirSync(downloadDir, { recursive: true });
300
326
  await downloadPackage(selectedPackage.asset, downloadDir);
301
327
  }
@@ -328,7 +354,7 @@ function getIdeCommand() {
328
354
  export function openInIDE(targetDir) {
329
355
  const ide = getIdeCommand();
330
356
  if (!ide) {
331
- console.log(chalk.yellow('⚠️ No supported IDE found'));
357
+ log(chalk.yellow('⚠️ No supported IDE found'));
332
358
  return;
333
359
  }
334
360
 
@@ -343,17 +369,26 @@ export function openInIDE(targetDir) {
343
369
  shell: process.platform === 'win32'
344
370
  }).unref();
345
371
 
346
- const args2 = ide.cmd === 'code-server'
347
- ? ['./readme.md', '--open']
348
- : ['./README.md'];
349
-
350
- spawn(ide.cmd, args2, {
351
- detached: true,
352
- stdio: 'ignore',
353
- shell: process.platform === 'win32'
354
- }).unref();
372
+ // open readme after 3 seconds
373
+ setTimeout(() => {
355
374
 
356
- console.log(chalk.green(`🚀 Opening ${path.basename(targetDir)} in ${ide.name}`));
375
+ const readme = fs.existsSync('./readme.md') ? './readme.md' :
376
+ fs.existsSync('./Readme.md') ? './Readme.md' :
377
+ fs.existsSync('./README.md') ? './README.md' :
378
+ fs.existsSync('./package.json') ? './package.json' :
379
+ null;
380
+
381
+ if (readme)
382
+ spawn(ide.cmd, ide.cmd === 'code-server'
383
+ ? [readme, '--open']
384
+ : [readme], {
385
+ detached: true,
386
+ stdio: 'ignore',
387
+ shell: process.platform === 'win32'
388
+ }).unref();
389
+ }, 3000);
390
+
391
+ log(chalk.green(`🚀 Opening ${path.basename(targetDir)} in ${ide.name}`));
357
392
  } catch (error) {
358
393
  console.error(chalk.red(`❌ Failed to open ${ide.name}:`), error.message);
359
394
  }
@@ -413,14 +448,14 @@ export async function installDependencies(targetDir) {
413
448
  // Run detections and installations
414
449
  Object.entries(detectors).forEach(([name, check]) => {
415
450
  if (check()) {
416
- console.log(chalk.yellow(`⚙️ Detected ${name} project`));
451
+ // log(chalk.yellow(`⚙️ Detected ${name} project`));
417
452
  installers[name]?.();
418
453
  }
419
454
  });
420
455
  }
421
456
 
422
457
  function runCommand(cmd) {
423
- console.log(chalk.cyan(`🚀 Running: ${cmd}`));
458
+ log(chalk.cyan(`🚀 Running: ${cmd}`));
424
459
  try {
425
460
  execSync(cmd, { stdio: 'inherit' });
426
461
  } catch (error) {
@@ -430,24 +465,26 @@ function runCommand(cmd) {
430
465
 
431
466
  export async function searchRepositories(query) {
432
467
  try {
433
- const response = await axios.get(GITHUB_API, {
434
- params: {
435
- q: `${query} in:name`,
436
- sort: 'stars',
437
- order: 'desc',
438
- per_page: RESULTS_PER_PAGE
439
- },
468
+ const response = await grab(GITHUB_API, {
469
+ q: `${query} in:name`,
470
+ sort: 'stars',
471
+ order: 'desc',
472
+ per_page: RESULTS_PER_PAGE,
473
+ debug: false,
440
474
  headers: TOKEN ? { Authorization: `token ${TOKEN}` } : {}
441
475
  });
442
-
476
+
477
+ if (response.error || !response.items)
478
+ return log("No response")
479
+
443
480
  // Check for releases for each repository
444
481
  const reposWithReleases = await Promise.all(
445
- response.data.items.map(async (repo) => {
482
+ response.items.map(async (repo) => {
446
483
  const releases = await getRepositoryReleases(repo.owner.login, repo.name);
447
484
  const currentPlatform = getCurrentPlatform();
448
485
  const compatibleReleases = filterReleasesByPlatform(releases, currentPlatform);
449
486
  const categorizedReleases = categorizeReleasesByPlatform(releases);
450
-
487
+
451
488
  return {
452
489
  ...repo,
453
490
  hasReleases: releases.length > 0,
@@ -457,49 +494,50 @@ export async function searchRepositories(query) {
457
494
  };
458
495
  })
459
496
  );
460
-
497
+
461
498
  return reposWithReleases;
462
499
  } catch (error) {
463
- console.error(chalk.red('Search failed:'), error.response?.data?.message || error.message);
500
+ console.error(chalk.red('Search failed:'), error.message);
464
501
  process.exit(1);
465
502
  }
466
503
  }
467
504
 
468
505
  export async function downloadRepo(repo) {
469
- const parsed = gitUrlParse(`https://github.com/${repo}`);
506
+ const parsed = gitUrlParse(repo);
470
507
  const defaultDir = path.resolve(process.cwd(), parsed.name);
471
508
  const extractPath = getAvailableDirectoryName(defaultDir);
472
509
 
510
+ // if it picks up a larger owner name, slice to the last part
511
+ if (parsed.owner.includes('/'))
512
+ parsed.owner = parsed.owner.split('/').slice(-1).join('');
513
+
473
514
  fs.mkdirSync(extractPath, { recursive: true });
474
- console.log(chalk.blue(`📦 Downloading ${parsed.full_name} into ${path.basename(extractPath)}...`));
475
- let url = `https://api.github.com/repos/${parsed.owner}/${parsed.name}/tarball/${parsed.default_branch || 'main'}`;
515
+ log(chalk.blue(`📦 Downloading ${parsed.name} into ${path.basename(extractPath)}...`));
516
+ printLogo()
517
+
518
+ let url = `https://api.github.com/repos/${parsed.owner}/${parsed.name}/tarball/${parsed.default_branch || 'master'}`;
476
519
 
477
520
  try {
478
- let response;
479
- try {
480
- response = await axios({
481
- url,
482
- method: 'GET',
483
- responseType: 'stream',
484
- headers: TOKEN ? { Authorization: `token ${TOKEN}` } : {}
485
- });
486
- } catch (error) {
487
- url = url.replace("/main", "/master");
488
- response = await axios({
489
- url,
490
- method: 'GET',
491
- responseType: 'stream',
492
- headers: TOKEN ? { Authorization: `token ${TOKEN}` } : {}
493
- });
521
+
522
+ var params = {
523
+ headers: TOKEN ? { Authorization: `token ${TOKEN}` } : {},
524
+ onStream: async (res) => {
525
+
526
+ const nodeStream = (await import('stream'))?.Readable.fromWeb(res);
527
+ await new Promise((resolve, reject) => {
528
+ nodeStream.pipe(tar.x({
529
+ C: extractPath,
530
+ strip: 1
531
+ })).on('finish', resolve).on('error', reject);
532
+ });
533
+ }
494
534
  }
495
535
 
496
- await pipeline(
497
- response.data,
498
- tar.x({
499
- C: extractPath,
500
- strip: 1
501
- })
502
- );
536
+ var response = await grab(url, params);
537
+
538
+ if (response.error)
539
+ response = await grab(url.replace("/master", "/main"), params);
540
+
503
541
 
504
542
  setTimeout(() => {
505
543
  openInIDE(extractPath);
@@ -523,16 +561,16 @@ function getAvailableDirectoryName(basePath) {
523
561
  }
524
562
 
525
563
  async function main() {
564
+ printLogo()
526
565
  const args = process.argv.slice(2);
527
566
  if (!args.length) {
528
- console.log(chalk.yellow('Usage: gg <search-query>'));
567
+ log(chalk.yellow('Usage: gg <search-query>'));
529
568
  process.exit(1);
530
569
  }
531
570
 
532
571
  const query = args.join(' ');
533
572
  const currentPlatform = getCurrentPlatform();
534
-
535
- console.log(chalk.gray(`🖥️ Detected platform: ${currentPlatform.os} ${currentPlatform.arch}`));
573
+
536
574
 
537
575
  let repoUrl = null;
538
576
 
@@ -553,22 +591,23 @@ async function main() {
553
591
 
554
592
  const results = await searchRepositories(query);
555
593
 
556
- if (!results.length) {
557
- console.log(chalk.yellow('No repositories found'));
594
+ if (!results || !results.length) {
595
+ log(chalk.yellow('No repositories found'));
558
596
  return;
559
597
  }
560
598
 
599
+
561
600
  const { selectedRepo } = await inquirer.prompt({
562
601
  type: 'list',
563
602
  name: 'selectedRepo',
564
603
  message: 'Select a repository to download:',
565
604
  choices: results.map(repo => {
566
- const packageInfo = repo.hasCompatibleReleases
567
- ? chalk.green(' 📦 Packages available')
568
- : repo.hasReleases
569
- ? chalk.yellow(' 📦 Packages (other platforms)')
605
+ const packageInfo = repo.hasCompatibleReleases
606
+ ? chalk.green(' 📦 Packages available')
607
+ : repo.hasReleases
608
+ ? chalk.yellow(' 📦 Packages (other platforms)')
570
609
  : '';
571
-
610
+
572
611
  return {
573
612
  name: `${chalk.bold(repo.full_name)} - ${chalk.gray(repo.description || 'No description')}
574
613
  ${chalk.yellow(`★ ${repo.stargazers_count}`)} | ${chalk.blue(repo.language || 'Unknown')}${packageInfo}`,
@@ -578,7 +617,7 @@ async function main() {
578
617
  });
579
618
 
580
619
  // If the selected repo has any releases, show download options
581
- if (selectedRepo.hasReleases) {
620
+ if (selectedRepo.hasReleases || selectedRepo.hasCompatibleReleases) {
582
621
  const { downloadChoice } = await inquirer.prompt({
583
622
  type: 'list',
584
623
  name: 'downloadChoice',
@@ -595,11 +634,11 @@ async function main() {
595
634
  }
596
635
 
597
636
  if (downloadChoice === 'source' || downloadChoice === 'both') {
598
- await downloadRepo(selectedRepo.html_url);
637
+ await downloadRepo(selectedRepo.url);
599
638
  }
600
639
  } else {
601
640
  // No packages, just download source
602
- await downloadRepo(selectedRepo.html_url);
641
+ await downloadRepo(selectedRepo.url);
603
642
  }
604
643
  }
605
644
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git0",
3
- "version": "0.1.6",
3
+ "version": "0.2.2",
4
4
  "description": "A CLI manager for downloading GitHub repos.",
5
5
  "author": "vtempest",
6
6
  "license": "MIT",
@@ -10,11 +10,13 @@
10
10
  "download"
11
11
  ],
12
12
  "dependencies": {
13
- "axios": "^1.7.2",
14
- "chalk": "^5.3.0",
13
+ "axios": "^1.9.0",
14
+ "chalk": "^5.4.1",
15
15
  "git-url-parse": "^16.1.0",
16
- "inquirer": "^9.2.16",
17
- "tar": "^6.2.1"
16
+ "grab-api.js": "^0.9.120",
17
+ "inquirer": "^12.6.3",
18
+ "streamable": "^0.6.0",
19
+ "tar": "^7.4.3"
18
20
  },
19
21
  "scripts": {
20
22
  "demo": "bun gg.js react template",
package/readme.md CHANGED
@@ -26,27 +26,20 @@
26
26
  </p>
27
27
 
28
28
 
29
- # Git0: GitHub Zero-Step Repo Downloader
29
+ # Git0: Git Repo in Zero-Steps
30
30
 
31
- A fast and smart CLI tool to search, download, and instantly set up GitHub repositories with automatic dependency installation and IDE integration.
32
-
33
-
34
- ## 🚀 Installation
35
-
36
- ```bash
37
- npm install -g git0
38
- ```
39
-
40
- ```bash
41
- bun install -g git0
42
- ```
31
+ A fast and smart CLI tool to search, download source & releases for your system, and
32
+ instantly set up GitHub repositories with automatic dependency installation and IDE integration.
43
33
 
34
+ ![livepreview](https://i.imgur.com/Io3ukRC.gif)
44
35
  ![preview](https://i.imgur.com/K22NiBq.png)
45
36
 
37
+
46
38
  ## ✨ Features
47
39
 
48
40
  - **Search GitHub repositories** by name with fuzzy matching
49
41
  - **Download repositories** directly from GitHub URLs or owner/repo shortcuts
42
+ - **Get Releases** instantly download latest release for your system or all systems
50
43
  - **Automatic dependency detection** and installation for multiple project types
51
44
  - **Smart IDE integration** - automatically opens projects in your preferred editor
52
45
  - **Cross-platform support** - works on Windows, macOS, and Linux
@@ -58,6 +51,10 @@ bun install -g git0
58
51
 
59
52
  ### Search and Download
60
53
  ```bash
54
+ # install in bun or node
55
+ npm i -g git0
56
+ bun i -g git0
57
+
61
58
  # Search for repositories by name
62
59
  gg react starter
63
60
 
@@ -98,7 +95,7 @@ GG automatically detects and opens projects in your preferred IDE:
98
95
 
99
96
  ### GitHub Token (Optional)
100
97
 
101
- For higher API rate limits, set your GitHub token:
98
+ For higher API rate limits, set [your GitHub token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-fine-grained-personal-access-token):
102
99
 
103
100
  ```bash
104
101
  export GITHUB_TOKEN=your_github_token_here