git0 0.2.5 → 0.2.6
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/bun.lock +159 -0
- package/docs/404.html +2 -2
- package/docs/Footer/index.html +2 -2
- package/docs/assets/js/{22dd74f7.5e857962.js → 22dd74f7.040a4546.js} +1 -1
- package/docs/assets/js/4a829dc8.10d7db0a.js +1 -0
- package/docs/assets/js/{main.c71c09a5.js → main.6b27aee7.js} +2 -2
- package/docs/assets/js/{runtime~main.ef55418f.js → runtime~main.5c54c19c.js} +1 -1
- package/docs/functions/globals/index.html +18 -27
- package/docs/functions/index.html +2 -2
- package/docs/index.html +2 -2
- package/docs/lunr-index-1749612068805.json +1 -0
- package/docs/lunr-index.json +1 -1
- package/docs/search-doc-1749612068805.json +1 -0
- package/docs/search-doc.json +1 -1
- package/docs-config/.docusaurus/DONT-EDIT-THIS-FOLDER +5 -0
- package/docs-config/.docusaurus/client-manifest.json +247 -0
- package/docs-config/.docusaurus/client-modules.js +8 -0
- package/docs-config/.docusaurus/codeTranslations.json +1 -0
- package/docs-config/.docusaurus/docusaurus-lunr-search/default/__plugin.json +4 -0
- package/docs-config/.docusaurus/docusaurus-plugin-content-docs/default/__mdx-loader-dependency.json +1 -0
- package/docs-config/.docusaurus/docusaurus-plugin-content-docs/default/__plugin.json +4 -0
- package/docs-config/.docusaurus/docusaurus-plugin-content-docs/default/p/index-466.json +1 -0
- package/docs-config/.docusaurus/docusaurus-plugin-content-docs/default/site-src-functions-globals-md-4a8.json +19 -0
- package/docs-config/.docusaurus/docusaurus-plugin-content-docs/default/site-src-functions-index-md-c3a.json +19 -0
- package/docs-config/.docusaurus/docusaurus-plugin-content-docs/default/site-src-index-md-d14.json +21 -0
- package/docs-config/.docusaurus/docusaurus-plugin-content-pages/default/__plugin.json +4 -0
- package/docs-config/.docusaurus/docusaurus-plugin-google-gtag/default/__plugin.json +4 -0
- package/docs-config/.docusaurus/docusaurus.config.mjs +385 -0
- package/docs-config/.docusaurus/globalData.json +60 -0
- package/docs-config/.docusaurus/i18n.json +17 -0
- package/docs-config/.docusaurus/lunr.client.js +5 -0
- package/docs-config/.docusaurus/registry.js +14 -0
- package/docs-config/.docusaurus/routes.js +55 -0
- package/docs-config/.docusaurus/routesChunkNames.json +41 -0
- package/docs-config/.docusaurus/site-metadata.json +51 -0
- package/docs-config/.docusaurus/site-storage.json +4 -0
- package/docs-config/bun.lock +6139 -0
- package/docs-config/config/customize-docs.js +1 -1
- package/docs-config/src/functions/globals.md +25 -79
- package/docs-config/tsconfig.json +1 -1
- package/package.json +10 -7
- package/src/git0.js +380 -0
- package/src/github-api.js +473 -0
- package/docs/assets/js/4a829dc8.728a4445.js +0 -1
- package/docs/lunr-index-1749601667754.json +0 -1
- package/docs/search-doc-1749601667754.json +0 -1
- package/docs-config/tailwind.config.js +0 -13
- package/git0.js +0 -646
- /package/docs/assets/js/{main.c71c09a5.js.LICENSE.txt → main.6b27aee7.js.LICENSE.txt} +0 -0
package/git0.js
DELETED
|
@@ -1,646 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import inquirer from 'inquirer';
|
|
4
|
-
import chalk from 'chalk';
|
|
5
|
-
import { execSync, spawn } from 'child_process';
|
|
6
|
-
import grab from 'grab-api.js';
|
|
7
|
-
import * as tar from 'tar'
|
|
8
|
-
import { pipeline } from 'stream/promises';
|
|
9
|
-
import fs from 'fs';
|
|
10
|
-
import path from 'path';
|
|
11
|
-
import gitUrlParse from 'git-url-parse';
|
|
12
|
-
import os from 'os';
|
|
13
|
-
|
|
14
|
-
const GITHUB_API = 'https://api.github.com/search/repositories';
|
|
15
|
-
const RESULTS_PER_PAGE = 10;
|
|
16
|
-
const TOKEN = process.env.GITHUB_TOKEN;
|
|
17
|
-
|
|
18
|
-
const githubHelpUrl = 'https://github.com/settings/personal-access-tokens/new'
|
|
19
|
-
grab('', {
|
|
20
|
-
setDefaults: true,
|
|
21
|
-
debug: false,
|
|
22
|
-
onError: (error) => {
|
|
23
|
-
if (error.includes('403')) {
|
|
24
|
-
log(chalk.red('Rate limit exceeded. Please set env var GITHUB_TOKEN. Help:\n' + githubHelpUrl));
|
|
25
|
-
process.exit(1);
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
function printLogo() {
|
|
33
|
-
console.log(chalk.cyan(` ___
|
|
34
|
-
__ _(_)‾|_ / _ \\
|
|
35
|
-
/ _ | | __| | | |
|
|
36
|
-
| (_| | | |_| |_| |
|
|
37
|
-
\\__, |_|\\__|\\___/
|
|
38
|
-
|___/`))
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Detect current operating system and architecture
|
|
42
|
-
function getCurrentPlatform() {
|
|
43
|
-
const platform = os.platform();
|
|
44
|
-
const arch = os.arch();
|
|
45
|
-
|
|
46
|
-
const platformMap = {
|
|
47
|
-
'win32': 'windows',
|
|
48
|
-
'darwin': 'macos',
|
|
49
|
-
'linux': 'linux'
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
const archMap = {
|
|
53
|
-
'x64': 'x86_64',
|
|
54
|
-
'arm64': 'arm64',
|
|
55
|
-
'arm': 'arm',
|
|
56
|
-
'ia32': 'i386'
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
return {
|
|
60
|
-
os: platformMap[platform] || platform,
|
|
61
|
-
arch: archMap[arch] || arch,
|
|
62
|
-
platform,
|
|
63
|
-
architecture: arch
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Get releases for a repository
|
|
68
|
-
async function getRepositoryReleases(owner, repo) {
|
|
69
|
-
try {
|
|
70
|
-
const response = await grab(`https://api.github.com/repos/${owner}/${repo}/releases`, {
|
|
71
|
-
headers: TOKEN ? { Authorization: `token ${TOKEN}` } : {},
|
|
72
|
-
per_page: 5 // Get latest 5 releases
|
|
73
|
-
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
return response;
|
|
77
|
-
} catch (error) {
|
|
78
|
-
return [];
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Categorize releases by platform
|
|
83
|
-
function categorizeReleasesByPlatform(releases) {
|
|
84
|
-
const platformKeywords = {
|
|
85
|
-
windows: ['win', 'windows', 'win32', 'win64', '.exe', '.msi'],
|
|
86
|
-
macos: ['mac', 'macos', 'darwin', 'osx', '.dmg', '.pkg'],
|
|
87
|
-
linux: ['linux', 'ubuntu', 'debian', '.deb', '.rpm', '.tar.gz', '.AppImage']
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
const archKeywords = {
|
|
91
|
-
x86_64: ['x86_64', 'x64', 'amd64', '64'],
|
|
92
|
-
arm64: ['arm64', 'aarch64'],
|
|
93
|
-
arm: ['arm', 'armv7'],
|
|
94
|
-
i386: ['i386', 'x86', '32']
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
const categorizedReleases = [];
|
|
98
|
-
|
|
99
|
-
Object.entries(releases).forEach(([key, release]) => {
|
|
100
|
-
const platformAssets = {
|
|
101
|
-
windows: [],
|
|
102
|
-
macos: [],
|
|
103
|
-
linux: [],
|
|
104
|
-
universal: []
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
release.assets.forEach(asset => {
|
|
108
|
-
const name = asset.name.toLowerCase();
|
|
109
|
-
let categorized = false;
|
|
110
|
-
|
|
111
|
-
// Check each platform
|
|
112
|
-
Object.entries(platformKeywords).forEach(([platform, keywords]) => {
|
|
113
|
-
if (keywords.some(keyword => name.includes(keyword.toLowerCase()))) {
|
|
114
|
-
// Determine architecture
|
|
115
|
-
let detectedArch = 'unknown';
|
|
116
|
-
Object.entries(archKeywords).forEach(([arch, archKeys]) => {
|
|
117
|
-
if (archKeys.some(archKey => name.includes(archKey.toLowerCase()))) {
|
|
118
|
-
detectedArch = arch;
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
platformAssets[platform].push({
|
|
123
|
-
...asset,
|
|
124
|
-
detectedArch,
|
|
125
|
-
platform
|
|
126
|
-
});
|
|
127
|
-
categorized = true;
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
// If not categorized, check for universal binaries
|
|
132
|
-
if (!categorized && (name.includes('universal') || name.includes('all') ||
|
|
133
|
-
(!name.includes('win') && !name.includes('mac') && !name.includes('linux')))) {
|
|
134
|
-
platformAssets.universal.push({
|
|
135
|
-
...asset,
|
|
136
|
-
detectedArch: 'universal',
|
|
137
|
-
platform: 'universal'
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
// Only include releases that have assets
|
|
143
|
-
const hasAssets = Object.values(platformAssets).some(assets => assets.length > 0);
|
|
144
|
-
if (hasAssets) {
|
|
145
|
-
categorizedReleases.push({
|
|
146
|
-
...release,
|
|
147
|
-
platformAssets
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
return categorizedReleases;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Filter releases for current platform (for compatibility indicator)
|
|
156
|
-
function filterReleasesByPlatform(releases, currentPlatform) {
|
|
157
|
-
const categorized = categorizeReleasesByPlatform(releases);
|
|
158
|
-
return categorized.filter(release =>
|
|
159
|
-
release.platformAssets[currentPlatform.os].length > 0 ||
|
|
160
|
-
release.platformAssets.universal.length > 0
|
|
161
|
-
);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
// Download and install package
|
|
166
|
-
async function downloadPackage(asset, targetDir) {
|
|
167
|
-
const fileName = asset.name;
|
|
168
|
-
const downloadPath = path.join(targetDir, fileName);
|
|
169
|
-
|
|
170
|
-
log(chalk.blue(`📦 Downloading ${fileName}...`));
|
|
171
|
-
printLogo()
|
|
172
|
-
try {
|
|
173
|
-
const response = await grab(asset.browser_download_url, {
|
|
174
|
-
headers: TOKEN ? { Authorization: `token ${TOKEN}` } : {},
|
|
175
|
-
onStream: async (res) => {
|
|
176
|
-
const nodeStream = (await import('stream'))?.Readable.fromWeb(res);
|
|
177
|
-
await new Promise((resolve, reject) => {
|
|
178
|
-
nodeStream.pipe(fs.createWriteStream(downloadPath)).on('finish', resolve).on('error', reject);
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
log(chalk.green(`✅ Downloaded ${fileName} to ${downloadPath}`));
|
|
184
|
-
|
|
185
|
-
// Try to make executable if it's a binary
|
|
186
|
-
if (process.platform !== 'win32' && !fileName.includes('.')) {
|
|
187
|
-
try {
|
|
188
|
-
fs.chmodSync(downloadPath, '755');
|
|
189
|
-
log(chalk.green(`✅ Made ${fileName} executable`));
|
|
190
|
-
} catch (error) {
|
|
191
|
-
log(chalk.yellow(`⚠️ Could not make ${fileName} executable`));
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// Provide installation instructions
|
|
196
|
-
provideInstallationInstructions(downloadPath, asset);
|
|
197
|
-
|
|
198
|
-
} catch (error) {
|
|
199
|
-
console.error(chalk.red(`❌ Failed to download ${fileName}:`), error.message);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// Provide platform-specific installation instructions
|
|
204
|
-
function provideInstallationInstructions(filePath, asset) {
|
|
205
|
-
const fileName = asset.name;
|
|
206
|
-
const platform = getCurrentPlatform();
|
|
207
|
-
|
|
208
|
-
if (platform.platform === 'win32') {
|
|
209
|
-
if (fileName.endsWith('.exe')) {
|
|
210
|
-
log(chalk.white(' Run the executable:'));
|
|
211
|
-
log(chalk.gray(` ${filePath}`));
|
|
212
|
-
} else if (fileName.endsWith('.msi')) {
|
|
213
|
-
log(chalk.white(' Install the MSI package:'));
|
|
214
|
-
log(chalk.gray(` msiexec /i "${filePath}"`));
|
|
215
|
-
}
|
|
216
|
-
} else if (platform.platform === 'darwin') {
|
|
217
|
-
if (fileName.endsWith('.dmg')) {
|
|
218
|
-
log(chalk.white(' Mount and install the DMG:'));
|
|
219
|
-
log(chalk.gray(` open "${filePath}"`));
|
|
220
|
-
} else if (fileName.endsWith('.pkg')) {
|
|
221
|
-
log(chalk.white(' Install the package:'));
|
|
222
|
-
log(chalk.gray(` sudo installer -pkg "${filePath}" -target /`));
|
|
223
|
-
}
|
|
224
|
-
} else {
|
|
225
|
-
if (fileName.endsWith('.deb')) {
|
|
226
|
-
log(chalk.white(' Install the DEB package:'));
|
|
227
|
-
log(chalk.gray(` sudo dpkg -i "${filePath}"`));
|
|
228
|
-
} else if (fileName.endsWith('.rpm')) {
|
|
229
|
-
log(chalk.white(' Install the RPM package:'));
|
|
230
|
-
log(chalk.gray(` sudo rpm -i "${filePath}"`));
|
|
231
|
-
} else if (fileName.endsWith('.AppImage')) {
|
|
232
|
-
log(chalk.white(' Run the AppImage:'));
|
|
233
|
-
log(chalk.gray(` chmod +x "${filePath}" && "${filePath}"`));
|
|
234
|
-
} else if (!fileName.includes('.')) {
|
|
235
|
-
log(chalk.white(' Binary is ready to use:'));
|
|
236
|
-
log(chalk.gray(` "${filePath}"`));
|
|
237
|
-
log(chalk.white(' Consider moving to PATH:'));
|
|
238
|
-
log(chalk.gray(` sudo mv "${filePath}" /usr/local/bin/`));
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// Show package selection menu organized by platform
|
|
244
|
-
async function showPackageMenu(selectedRepo) {
|
|
245
|
-
const currentPlatform = getCurrentPlatform();
|
|
246
|
-
const releaseChoices = [];
|
|
247
|
-
const limitReleases = 2;
|
|
248
|
-
|
|
249
|
-
// Add section headers and organize by platform
|
|
250
|
-
selectedRepo.allReleases.slice(0, limitReleases).forEach(release => {
|
|
251
|
-
const platforms = ['windows', 'macos', 'linux', 'universal'];
|
|
252
|
-
|
|
253
|
-
platforms.forEach(platform => {
|
|
254
|
-
const assets = release.platformAssets[platform];
|
|
255
|
-
if (assets.length > 0) {
|
|
256
|
-
// Add platform header
|
|
257
|
-
const platformEmoji = {
|
|
258
|
-
windows: '🪟',
|
|
259
|
-
macos: '🍎',
|
|
260
|
-
linux: '🐧',
|
|
261
|
-
universal: '🌐'
|
|
262
|
-
};
|
|
263
|
-
|
|
264
|
-
const platformName = {
|
|
265
|
-
windows: 'Windows',
|
|
266
|
-
macos: 'macOS',
|
|
267
|
-
linux: 'Linux',
|
|
268
|
-
universal: 'Universal'
|
|
269
|
-
};
|
|
270
|
-
|
|
271
|
-
const isCurrentPlatform = platform === currentPlatform.os || platform === 'universal';
|
|
272
|
-
const platformHeader = isCurrentPlatform
|
|
273
|
-
? chalk.green(`${platformEmoji[platform]} ${platformName[platform]} (Your Platform)`)
|
|
274
|
-
: chalk.gray(`${platformEmoji[platform]} ${platformName[platform]}`);
|
|
275
|
-
|
|
276
|
-
// Add separator if not first platform in this release
|
|
277
|
-
const needsSeparator = releaseChoices.length > 0 &&
|
|
278
|
-
!releaseChoices[releaseChoices.length - 1].name.includes('────');
|
|
279
|
-
|
|
280
|
-
if (needsSeparator) {
|
|
281
|
-
releaseChoices.push({
|
|
282
|
-
name: chalk.gray('────────────────────────────────'),
|
|
283
|
-
disabled: true
|
|
284
|
-
});
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
releaseChoices.push({
|
|
288
|
-
name: `${chalk.bold(release.tag_name)} - ${platformHeader}`,
|
|
289
|
-
disabled: true
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
// Add assets for this platform
|
|
293
|
-
assets.forEach(asset => {
|
|
294
|
-
const sizeStr = (asset.size / 1024 / 1024).toFixed(2);
|
|
295
|
-
const archInfo = asset.detectedArch !== 'unknown' && asset.detectedArch !== 'universal'
|
|
296
|
-
? chalk.cyan(`[${asset.detectedArch}]`)
|
|
297
|
-
: '';
|
|
298
|
-
|
|
299
|
-
const highlight = isCurrentPlatform ? chalk.white : chalk.gray;
|
|
300
|
-
|
|
301
|
-
releaseChoices.push({
|
|
302
|
-
name: ` ${highlight(`${asset.name} ${archInfo} (${sizeStr} MB)`)}`,
|
|
303
|
-
value: { release, asset }
|
|
304
|
-
});
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
|
-
});
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
if (releaseChoices.filter(choice => !choice.disabled).length === 0) {
|
|
311
|
-
log(chalk.yellow('No packages found for download.'));
|
|
312
|
-
return;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
const { selectedPackage } = await inquirer.prompt({
|
|
316
|
-
type: 'list',
|
|
317
|
-
name: 'selectedPackage',
|
|
318
|
-
message: 'Select a package to download:',
|
|
319
|
-
choices: releaseChoices,
|
|
320
|
-
pageSize: 15
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
const downloadDir = path.resolve(process.cwd());
|
|
324
|
-
fs.mkdirSync(downloadDir, { recursive: true });
|
|
325
|
-
await downloadPackage(selectedPackage.asset, downloadDir);
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
function getIdeCommand() {
|
|
329
|
-
const ides = [
|
|
330
|
-
{ name: 'Cursor', cmd: 'cursor' },
|
|
331
|
-
{ name: 'Windsurf', cmd: 'windsurf' },
|
|
332
|
-
{ name: 'VSCode', cmd: 'code' },
|
|
333
|
-
{ name: 'Code Server', cmd: 'code-server' },
|
|
334
|
-
{ name: 'Neovim', cmd: 'nvim' },
|
|
335
|
-
{ name: 'Webstorm', cmd: 'webstorm' }
|
|
336
|
-
];
|
|
337
|
-
|
|
338
|
-
for (const ide of ides) {
|
|
339
|
-
try {
|
|
340
|
-
execSync(
|
|
341
|
-
process.platform === 'win32'
|
|
342
|
-
? `where ${ide.cmd}`
|
|
343
|
-
: `command -v ${ide.cmd}`,
|
|
344
|
-
{ stdio: 'ignore' }
|
|
345
|
-
);
|
|
346
|
-
return ide;
|
|
347
|
-
} catch (error) {
|
|
348
|
-
continue;
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
return null;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
export function openInIDE(targetDir) {
|
|
355
|
-
const ide = getIdeCommand();
|
|
356
|
-
if (!ide) {
|
|
357
|
-
log(chalk.yellow('⚠️ No supported IDE found'));
|
|
358
|
-
return;
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
try {
|
|
362
|
-
const args = ide.cmd === 'code-server'
|
|
363
|
-
? [targetDir, '--open']
|
|
364
|
-
: [targetDir];
|
|
365
|
-
|
|
366
|
-
spawn(ide.cmd, args, {
|
|
367
|
-
detached: true,
|
|
368
|
-
stdio: 'ignore',
|
|
369
|
-
shell: process.platform === 'win32'
|
|
370
|
-
}).unref();
|
|
371
|
-
|
|
372
|
-
// open readme after 3 seconds
|
|
373
|
-
setTimeout(() => {
|
|
374
|
-
|
|
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}`));
|
|
392
|
-
} catch (error) {
|
|
393
|
-
console.error(chalk.red(`❌ Failed to open ${ide.name}:`), error.message);
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
export async function installDependencies(targetDir) {
|
|
398
|
-
process.chdir(targetDir);
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
// Project type detection
|
|
402
|
-
const detectors = {
|
|
403
|
-
nodejs: () => fs.existsSync('package.json'),
|
|
404
|
-
docker: () => fs.existsSync('Dockerfile'),
|
|
405
|
-
python: () => fs.existsSync('requirements.txt') || fs.existsSync('setup.py'),
|
|
406
|
-
rust: () => fs.existsSync('Cargo.toml'),
|
|
407
|
-
go: () => fs.existsSync('go.mod')
|
|
408
|
-
};
|
|
409
|
-
|
|
410
|
-
// Install commands for each project type
|
|
411
|
-
const installers = {
|
|
412
|
-
nodejs: () => {
|
|
413
|
-
try {
|
|
414
|
-
execSync(
|
|
415
|
-
process.platform === 'win32'
|
|
416
|
-
? `where bun`
|
|
417
|
-
: `command -v bun`,
|
|
418
|
-
{ stdio: 'ignore' }
|
|
419
|
-
);
|
|
420
|
-
runCommand('bun install');
|
|
421
|
-
runCommand('bun run dev; bun run start');
|
|
422
|
-
} catch (error) {
|
|
423
|
-
runCommand('npm install');
|
|
424
|
-
runCommand('npm run dev; npm run start');
|
|
425
|
-
}
|
|
426
|
-
},
|
|
427
|
-
docker: () => {
|
|
428
|
-
if (fs.existsSync('docker-compose.yml')) {
|
|
429
|
-
runCommand('sudo docker-compose up -d');
|
|
430
|
-
} else if (fs.existsSync('Dockerfile')) {
|
|
431
|
-
runCommand('sudo docker build -t project .');
|
|
432
|
-
}
|
|
433
|
-
},
|
|
434
|
-
python: () => {
|
|
435
|
-
runCommand('python -m venv .venv');
|
|
436
|
-
runCommand('source .venv/bin/activate');
|
|
437
|
-
|
|
438
|
-
if (fs.existsSync('requirements.txt')) {
|
|
439
|
-
runCommand('pip install -r requirements.txt');
|
|
440
|
-
}
|
|
441
|
-
if (fs.existsSync('setup.py')) {
|
|
442
|
-
runCommand('pip install -e .');
|
|
443
|
-
}
|
|
444
|
-
},
|
|
445
|
-
rust: () => runCommand('cargo build'),
|
|
446
|
-
go: () => runCommand('go mod tidy')
|
|
447
|
-
};
|
|
448
|
-
|
|
449
|
-
// Run detections and installations
|
|
450
|
-
Object.entries(detectors).forEach(([name, check]) => {
|
|
451
|
-
if (check()) {
|
|
452
|
-
// log(chalk.yellow(`⚙️ Detected ${name} project`));
|
|
453
|
-
installers[name]?.();
|
|
454
|
-
}
|
|
455
|
-
});
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
function runCommand(cmd) {
|
|
459
|
-
log(chalk.cyan(`🚀 Running: ${cmd}`));
|
|
460
|
-
try {
|
|
461
|
-
execSync(cmd, { stdio: 'inherit' });
|
|
462
|
-
} catch (error) {
|
|
463
|
-
console.error(chalk.red(`❌ Failed: ${cmd}`));
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
export async function searchRepositories(query) {
|
|
468
|
-
try {
|
|
469
|
-
const response = await grab(GITHUB_API, {
|
|
470
|
-
q: `${query} in:name`,
|
|
471
|
-
sort: 'stars',
|
|
472
|
-
order: 'desc',
|
|
473
|
-
per_page: RESULTS_PER_PAGE,
|
|
474
|
-
debug: false,
|
|
475
|
-
headers: TOKEN ? { Authorization: `token ${TOKEN}` } : {}
|
|
476
|
-
});
|
|
477
|
-
|
|
478
|
-
if (response.error || !response.items)
|
|
479
|
-
return log("No response")
|
|
480
|
-
|
|
481
|
-
// Check for releases for each repository
|
|
482
|
-
const reposWithReleases = await Promise.all(
|
|
483
|
-
response.items.map(async (repo) => {
|
|
484
|
-
const releases = await getRepositoryReleases(repo.owner.login, repo.name);
|
|
485
|
-
const currentPlatform = getCurrentPlatform();
|
|
486
|
-
const compatibleReleases = filterReleasesByPlatform(releases, currentPlatform);
|
|
487
|
-
const categorizedReleases = categorizeReleasesByPlatform(releases);
|
|
488
|
-
|
|
489
|
-
return {
|
|
490
|
-
...repo,
|
|
491
|
-
hasReleases: releases.length > 0,
|
|
492
|
-
hasCompatibleReleases: compatibleReleases.length > 0,
|
|
493
|
-
releases: compatibleReleases,
|
|
494
|
-
allReleases: categorizedReleases
|
|
495
|
-
};
|
|
496
|
-
})
|
|
497
|
-
);
|
|
498
|
-
|
|
499
|
-
return reposWithReleases;
|
|
500
|
-
} catch (error) {
|
|
501
|
-
console.error(chalk.red('Search failed:'), error.message);
|
|
502
|
-
process.exit(1);
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
export async function downloadRepo(repo) {
|
|
507
|
-
const parsed = gitUrlParse(repo);
|
|
508
|
-
const defaultDir = path.resolve(process.cwd(), parsed.name);
|
|
509
|
-
const extractPath = getAvailableDirectoryName(defaultDir);
|
|
510
|
-
|
|
511
|
-
// if it picks up a larger owner name, slice to the last part
|
|
512
|
-
if (parsed.owner.includes('/'))
|
|
513
|
-
parsed.owner = parsed.owner.split('/').slice(-1).join('');
|
|
514
|
-
|
|
515
|
-
fs.mkdirSync(extractPath, { recursive: true });
|
|
516
|
-
log(chalk.blue(`📦 Downloading ${parsed.name} into ${path.basename(extractPath)}...`));
|
|
517
|
-
printLogo()
|
|
518
|
-
|
|
519
|
-
let url = `https://api.github.com/repos/${parsed.owner}/${parsed.name}/tarball/${parsed.default_branch || 'master'}`;
|
|
520
|
-
|
|
521
|
-
try {
|
|
522
|
-
|
|
523
|
-
var params = {
|
|
524
|
-
headers: TOKEN ? { Authorization: `token ${TOKEN}` } : {},
|
|
525
|
-
onStream: async (res) => {
|
|
526
|
-
|
|
527
|
-
const nodeStream = (await import('stream'))?.Readable.fromWeb(res);
|
|
528
|
-
await new Promise((resolve, reject) => {
|
|
529
|
-
nodeStream.pipe(tar.x({
|
|
530
|
-
C: extractPath,
|
|
531
|
-
strip: 1
|
|
532
|
-
})).on('finish', resolve).on('error', reject);
|
|
533
|
-
});
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
var response = await grab(url, params);
|
|
538
|
-
|
|
539
|
-
if (response.error)
|
|
540
|
-
response = await grab(url.replace("/master", "/main"), params);
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
setTimeout(() => {
|
|
544
|
-
openInIDE(extractPath);
|
|
545
|
-
}, 1000);
|
|
546
|
-
installDependencies(extractPath);
|
|
547
|
-
} catch (error) {
|
|
548
|
-
console.error(chalk.red('Download failed:'), error.message);
|
|
549
|
-
process.exit(1);
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
function getAvailableDirectoryName(basePath) {
|
|
554
|
-
if (!fs.existsSync(basePath)) return basePath;
|
|
555
|
-
let counter = 2;
|
|
556
|
-
let newPath;
|
|
557
|
-
while (true) {
|
|
558
|
-
newPath = `${basePath}-${counter}`;
|
|
559
|
-
if (!fs.existsSync(newPath)) return newPath;
|
|
560
|
-
counter++;
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
async function main() {
|
|
565
|
-
printLogo()
|
|
566
|
-
const args = process.argv.slice(2);
|
|
567
|
-
if (!args.length) {
|
|
568
|
-
log(chalk.yellow('Usage: gg <search-query>'));
|
|
569
|
-
process.exit(1);
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
const query = args.join(' ');
|
|
573
|
-
const currentPlatform = getCurrentPlatform();
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
let repoUrl = null;
|
|
577
|
-
|
|
578
|
-
// Try parsing as a GitHub URL or shorthand
|
|
579
|
-
let parsed = null;
|
|
580
|
-
|
|
581
|
-
if (query.includes('github.com') || query.startsWith('git@github.com:') || query.startsWith('https://') || query.startsWith('git://')) {
|
|
582
|
-
parsed = gitUrlParse(query);
|
|
583
|
-
} else if (/^[\w-]+\/[\w.-]+$/.test(query)) {
|
|
584
|
-
// Shorthand owner/repo
|
|
585
|
-
parsed = gitUrlParse(`https://github.com/${query}`);
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
if (parsed && parsed.owner && parsed.name) {
|
|
589
|
-
await downloadRepo(parsed.href);
|
|
590
|
-
return;
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
const results = await searchRepositories(query);
|
|
594
|
-
|
|
595
|
-
if (!results || !results.length) {
|
|
596
|
-
log(chalk.yellow('No repositories found'));
|
|
597
|
-
return;
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
const { selectedRepo } = await inquirer.prompt({
|
|
602
|
-
type: 'list',
|
|
603
|
-
name: 'selectedRepo',
|
|
604
|
-
message: 'Select a repository to download:',
|
|
605
|
-
choices: results.map(repo => {
|
|
606
|
-
const packageInfo = repo.hasCompatibleReleases
|
|
607
|
-
? chalk.green(' 📦 Packages available')
|
|
608
|
-
: repo.hasReleases
|
|
609
|
-
? chalk.yellow(' 📦 Packages (other platforms)')
|
|
610
|
-
: '';
|
|
611
|
-
|
|
612
|
-
return {
|
|
613
|
-
name: `${chalk.bold(repo.full_name)} - ${chalk.gray(repo.description || 'No description')}
|
|
614
|
-
${chalk.yellow(`★ ${repo.stargazers_count}`)} | ${chalk.blue(repo.language || 'Unknown')}${packageInfo}`,
|
|
615
|
-
value: repo
|
|
616
|
-
};
|
|
617
|
-
})
|
|
618
|
-
});
|
|
619
|
-
|
|
620
|
-
// If the selected repo has any releases, show download options
|
|
621
|
-
if (selectedRepo.hasReleases || selectedRepo.hasCompatibleReleases) {
|
|
622
|
-
const { downloadChoice } = await inquirer.prompt({
|
|
623
|
-
type: 'list',
|
|
624
|
-
name: 'downloadChoice',
|
|
625
|
-
message: 'This repository has downloadable packages. What would you like to do?',
|
|
626
|
-
choices: [
|
|
627
|
-
{ name: '📦 Download package/binary', value: 'package' },
|
|
628
|
-
{ name: '📂 Download source code', value: 'source' },
|
|
629
|
-
{ name: '📦📂 Download both package and source', value: 'both' }
|
|
630
|
-
]
|
|
631
|
-
});
|
|
632
|
-
|
|
633
|
-
if (downloadChoice === 'package' || downloadChoice === 'both') {
|
|
634
|
-
await showPackageMenu(selectedRepo);
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
if (downloadChoice === 'source' || downloadChoice === 'both') {
|
|
638
|
-
await downloadRepo(selectedRepo.url);
|
|
639
|
-
}
|
|
640
|
-
} else {
|
|
641
|
-
// No packages, just download source
|
|
642
|
-
await downloadRepo(selectedRepo.url);
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
main();
|
|
File without changes
|