git0 0.2.4 → 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.
Files changed (95) hide show
  1. package/bun.lock +159 -0
  2. package/docs/404.html +26 -3
  3. package/docs/Footer/index.html +26 -3
  4. package/docs/assets/css/styles.3dd00f8d.css +1 -0
  5. package/docs/assets/js/17896441.79ceefeb.js +1 -0
  6. package/docs/assets/js/1df93b7f.dfdf0ef3.js +2 -0
  7. package/docs/assets/js/1df93b7f.dfdf0ef3.js.LICENSE.txt +6 -0
  8. package/docs/assets/js/22dd74f7.040a4546.js +1 -0
  9. package/docs/assets/js/237.daf7efdf.js +1 -0
  10. package/docs/assets/js/278.1628756c.js +1 -0
  11. package/docs/assets/js/4a829dc8.10d7db0a.js +1 -0
  12. package/docs/assets/js/577.78325c02.js +1 -0
  13. package/docs/assets/js/591.2156b33d.js +2 -0
  14. package/docs/assets/js/591.2156b33d.js.LICENSE.txt +61 -0
  15. package/docs/assets/js/5e95c892.bd6c1093.js +1 -0
  16. package/docs/assets/js/a7456010.3054873b.js +1 -0
  17. package/docs/assets/js/a7bd4aaa.87229397.js +1 -0
  18. package/docs/assets/js/a94703ab.6d39a596.js +1 -0
  19. package/docs/assets/js/aba21aa0.dfebd789.js +1 -0
  20. package/docs/assets/js/ac46717f.32345167.js +2 -0
  21. package/docs/assets/js/ac46717f.32345167.js.LICENSE.txt +6 -0
  22. package/docs/assets/js/c3a618e1.50a89e4d.js +1 -0
  23. package/docs/assets/js/d140250a.62af53aa.js +1 -0
  24. package/docs/assets/js/main.6b27aee7.js +2 -0
  25. package/docs/assets/js/main.6b27aee7.js.LICENSE.txt +68 -0
  26. package/docs/assets/js/runtime~main.5c54c19c.js +1 -0
  27. package/docs/functions/globals/index.html +47 -3
  28. package/docs/functions/index.html +73 -3
  29. package/docs/index.html +26 -3
  30. package/docs/lunr-index-1749612068805.json +1 -0
  31. package/docs/lunr-index.json +1 -1
  32. package/docs/search-doc-1749612068805.json +1 -0
  33. package/docs/search-doc.json +1 -1
  34. package/docs/sitemap.xml +1 -1
  35. package/docs-config/.docusaurus/DONT-EDIT-THIS-FOLDER +5 -0
  36. package/docs-config/.docusaurus/client-manifest.json +247 -0
  37. package/docs-config/.docusaurus/client-modules.js +8 -0
  38. package/docs-config/.docusaurus/codeTranslations.json +1 -0
  39. package/docs-config/.docusaurus/docusaurus-lunr-search/default/__plugin.json +4 -0
  40. package/docs-config/.docusaurus/docusaurus-plugin-content-docs/default/__mdx-loader-dependency.json +1 -0
  41. package/docs-config/.docusaurus/docusaurus-plugin-content-docs/default/__plugin.json +4 -0
  42. package/docs-config/.docusaurus/docusaurus-plugin-content-docs/default/p/index-466.json +1 -0
  43. package/docs-config/.docusaurus/docusaurus-plugin-content-docs/default/site-src-functions-globals-md-4a8.json +19 -0
  44. package/docs-config/.docusaurus/docusaurus-plugin-content-docs/default/site-src-functions-index-md-c3a.json +19 -0
  45. package/docs-config/.docusaurus/docusaurus-plugin-content-docs/default/site-src-index-md-d14.json +21 -0
  46. package/docs-config/.docusaurus/docusaurus-plugin-content-pages/default/__plugin.json +4 -0
  47. package/docs-config/.docusaurus/docusaurus-plugin-google-gtag/default/__plugin.json +4 -0
  48. package/docs-config/.docusaurus/docusaurus.config.mjs +385 -0
  49. package/docs-config/.docusaurus/globalData.json +60 -0
  50. package/docs-config/.docusaurus/i18n.json +17 -0
  51. package/docs-config/.docusaurus/lunr.client.js +5 -0
  52. package/docs-config/.docusaurus/registry.js +14 -0
  53. package/docs-config/.docusaurus/routes.js +55 -0
  54. package/docs-config/.docusaurus/routesChunkNames.json +41 -0
  55. package/docs-config/.docusaurus/site-metadata.json +51 -0
  56. package/docs-config/.docusaurus/site-storage.json +4 -0
  57. package/docs-config/bun.lock +6139 -0
  58. package/docs-config/config/customize-docs.js +3 -4
  59. package/docs-config/docusaurus.config.ts +5 -3
  60. package/docs-config/src/functions/globals.md +25 -79
  61. package/docs-config/src/functions/index.md +25 -25
  62. package/docs-config/src/index.md +26 -25
  63. package/docs-config/src/pages/Footer.tsx +63 -53
  64. package/docs-config/src/pages/index.tsx +139 -7
  65. package/docs-config/tsconfig.json +3 -2
  66. package/package.json +10 -7
  67. package/readme.md +26 -25
  68. package/src/git0.js +380 -0
  69. package/src/github-api.js +473 -0
  70. package/docs/assets/css/styles.36dc19db.css +0 -1
  71. package/docs/assets/js/17896441.859f1a34.js +0 -1
  72. package/docs/assets/js/196.f2323311.js +0 -1
  73. package/docs/assets/js/1df93b7f.b886b8fe.js +0 -1
  74. package/docs/assets/js/22dd74f7.9915da9b.js +0 -1
  75. package/docs/assets/js/294.067938e2.js +0 -101
  76. package/docs/assets/js/4a829dc8.22596b01.js +0 -1
  77. package/docs/assets/js/5e95c892.a050489a.js +0 -1
  78. package/docs/assets/js/61.78a4a5cd.js +0 -1
  79. package/docs/assets/js/66a0d7f4.a57db0e1.js +0 -1
  80. package/docs/assets/js/990.0223f00f.js +0 -1
  81. package/docs/assets/js/a7456010.eaa69c24.js +0 -1
  82. package/docs/assets/js/a7bd4aaa.6bf60595.js +0 -1
  83. package/docs/assets/js/a94703ab.233c731c.js +0 -1
  84. package/docs/assets/js/aba21aa0.3975ceaf.js +0 -1
  85. package/docs/assets/js/ac46717f.20537802.js +0 -1
  86. package/docs/assets/js/c3a618e1.b288e34c.js +0 -1
  87. package/docs/assets/js/d140250a.42194f66.js +0 -1
  88. package/docs/assets/js/main.9107865a.js +0 -38
  89. package/docs/assets/js/runtime~main.d42315b4.js +0 -1
  90. package/docs/customize-home/index.html +0 -4
  91. package/docs/lunr-index-1749598895331.json +0 -1
  92. package/docs/search-doc-1749598895331.json +0 -1
  93. package/docs-config/src/pages/customize-home.tsx +0 -147
  94. package/docs-config/tailwind.config.js +0 -13
  95. package/git0.js +0 -646
package/src/git0.js ADDED
@@ -0,0 +1,380 @@
1
+ #!/usr/bin/env node
2
+ import inquirer from 'inquirer';
3
+ import chalk from 'chalk';
4
+ import { execSync, spawn } from 'child_process';
5
+ import fs from 'fs';
6
+ import path from 'path';
7
+ import ora from ora;
8
+ import GithubAPI from './github-api'
9
+
10
+ const Github = new GithubAPI({ debug: false })
11
+
12
+ /**
13
+ * Prints the git0 ASCII logo to the console
14
+ */
15
+ function printLogo() {
16
+ console.log(chalk.cyan(` ___
17
+ __ _(_)‾|_ / _ \\
18
+ / _ | | __| | | |
19
+ | (_| | | |_| |_| |
20
+ \\__, |_|\\__|\\___/
21
+ |___/`))
22
+ }
23
+
24
+ /**
25
+ * Shows an interactive menu for selecting packages from GitHub releases
26
+ * Organizes packages by platform and highlights the user's current platform
27
+ * @param {Object} selectedRepo - The selected repository object containing release information
28
+ * @param {Array} selectedRepo.allReleases - Array of release objects
29
+ * @param {string} selectedRepo.allReleases[].tag_name - Release tag name
30
+ * @param {Object} selectedRepo.allReleases[].platformAssets - Assets organized by platform
31
+ * @returns {Promise<void>}
32
+ */
33
+ async function showPackageMenu(selectedRepo) {
34
+ const currentPlatform = Github.getCurrentPlatform();
35
+ const releaseChoices = [];
36
+ const limitReleases = 2;
37
+
38
+ // Add section headers and organize by platform
39
+ selectedRepo.allReleases.slice(0, limitReleases).forEach(release => {
40
+ const platforms = ['windows', 'macos', 'linux', 'universal'];
41
+
42
+ platforms.forEach(platform => {
43
+ const assets = release.platformAssets[platform];
44
+ if (assets.length > 0) {
45
+ // Add platform header
46
+ const platformEmoji = {
47
+ windows: '🪟',
48
+ macos: '🍎',
49
+ linux: '🐧',
50
+ universal: '🌐'
51
+ };
52
+
53
+ const platformName = {
54
+ windows: 'Windows',
55
+ macos: 'macOS',
56
+ linux: 'Linux',
57
+ universal: 'Universal'
58
+ };
59
+
60
+ const isCurrentPlatform = platform === currentPlatform.os || platform === 'universal';
61
+ const platformHeader = isCurrentPlatform
62
+ ? chalk.green(`${platformEmoji[platform]} ${platformName[platform]} (Your Platform)`)
63
+ : chalk.gray(`${platformEmoji[platform]} ${platformName[platform]}`);
64
+
65
+ // Add separator if not first platform in this release
66
+ const needsSeparator = releaseChoices.length > 0 &&
67
+ !releaseChoices[releaseChoices.length - 1].name.includes('────');
68
+
69
+ if (needsSeparator) {
70
+ releaseChoices.push({
71
+ name: chalk.gray('────────────────────────────────'),
72
+ disabled: true
73
+ });
74
+ }
75
+
76
+ releaseChoices.push({
77
+ name: `${chalk.bold(release.tag_name)} - ${platformHeader}`,
78
+ disabled: true
79
+ });
80
+
81
+ // Add assets for this platform
82
+ assets.forEach(asset => {
83
+ const sizeStr = (asset.size / 1024 / 1024).toFixed(2);
84
+ const archInfo = asset.detectedArch !== 'unknown' && asset.detectedArch !== 'universal'
85
+ ? chalk.cyan(`[${asset.detectedArch}]`)
86
+ : '';
87
+
88
+ const highlight = isCurrentPlatform ? chalk.white : chalk.gray;
89
+
90
+ releaseChoices.push({
91
+ name: ` ${highlight(`${asset.name} ${archInfo} (${sizeStr} MB)`)}`,
92
+ value: { release, asset }
93
+ });
94
+ });
95
+ }
96
+ });
97
+ });
98
+
99
+ if (releaseChoices.filter(choice => !choice.disabled).length === 0) {
100
+ log(chalk.yellow('No packages found for download.'));
101
+ return;
102
+ }
103
+
104
+ const { selectedPackage } = await inquirer.prompt({
105
+ type: 'list',
106
+ name: 'selectedPackage',
107
+ message: 'Select a package to download:',
108
+ choices: releaseChoices,
109
+ pageSize: 15
110
+ });
111
+
112
+ const downloadDir = path.resolve(process.cwd());
113
+ fs.mkdirSync(downloadDir, { recursive: true });
114
+
115
+ const downloadPath = path.join(downloadDir, asset.name);
116
+ await Github.downloadPackage(selectedPackage.asset.browser_download_url, downloadPath);
117
+ }
118
+
119
+ /**
120
+ * Detects available IDE/editor commands on the system
121
+ * Checks for popular code editors in priority order
122
+ * @returns {Object|null} IDE object with name and command, or null if none found
123
+ * @returns {string} returns.name - Human-readable name of the IDE
124
+ * @returns {string} returns.cmd - Command to execute the IDE
125
+ */
126
+ function getIdeCommand() {
127
+ const ides = [
128
+ { name: 'Cursor', cmd: 'cursor' },
129
+ { name: 'Windsurf', cmd: 'windsurf' },
130
+ { name: 'VSCode', cmd: 'code' },
131
+ { name: 'Code Server', cmd: 'code-server' },
132
+ { name: 'Neovim', cmd: 'nvim' },
133
+ { name: 'Webstorm', cmd: 'webstorm' }
134
+ ];
135
+
136
+ for (const ide of ides) {
137
+ try {
138
+ execSync(
139
+ process.platform === 'win32'
140
+ ? `where ${ide.cmd}`
141
+ : `command -v ${ide.cmd}`,
142
+ { stdio: 'ignore' }
143
+ );
144
+ return ide;
145
+ } catch (error) {
146
+ continue;
147
+ }
148
+ }
149
+ return null;
150
+ }
151
+
152
+ /**
153
+ * Opens a directory in the first available IDE/editor
154
+ * Also attempts to open a README or package.json file after 3 seconds
155
+ * @export
156
+ * @param {string} targetDir - Path to the directory to open
157
+ */
158
+ export function openInIDE(targetDir) {
159
+ const ide = getIdeCommand();
160
+ if (!ide) {
161
+ log(chalk.yellow('⚠️ No supported IDE found'));
162
+ return;
163
+ }
164
+
165
+ try {
166
+ const args = ide.cmd === 'code-server'
167
+ ? [targetDir, '--open']
168
+ : [targetDir];
169
+
170
+ spawn(ide.cmd, args, {
171
+ detached: true,
172
+ stdio: 'ignore',
173
+ shell: process.platform === 'win32'
174
+ }).unref();
175
+
176
+ // open readme after 3 seconds
177
+ setTimeout(() => {
178
+
179
+ const readme = fs.existsSync('./readme.md') ? './readme.md' :
180
+ fs.existsSync('./Readme.md') ? './Readme.md' :
181
+ fs.existsSync('./README.md') ? './README.md' :
182
+ fs.existsSync('./package.json') ? './package.json' :
183
+ null;
184
+
185
+ if (readme)
186
+ spawn(ide.cmd, ide.cmd === 'code-server'
187
+ ? [readme, '--open']
188
+ : [readme], {
189
+ detached: true,
190
+ stdio: 'ignore',
191
+ shell: process.platform === 'win32'
192
+ }).unref();
193
+ }, 3000);
194
+
195
+ log(chalk.green(`🚀 Opening ${path.basename(targetDir)} in ${ide.name}`));
196
+ } catch (error) {
197
+ // console.error(chalk.red(`❌ Failed to open ${ide.name}:`), error.message);
198
+ }
199
+ }
200
+
201
+ /**
202
+ * Automatically detects project type and installs dependencies
203
+ * Supports Node.js, Docker, Python, Rust, and Go projects
204
+ * @export
205
+ * @async
206
+ * @param {string} targetDir - Path to the project directory
207
+ */
208
+ export async function installDependencies(targetDir) {
209
+ process.chdir(targetDir);
210
+
211
+ // Project type detection
212
+ const detectors = {
213
+ nodejs: () => fs.existsSync('package.json'),
214
+ docker: () => fs.existsSync('Dockerfile'),
215
+ python: () => fs.existsSync('requirements.txt') || fs.existsSync('setup.py'),
216
+ rust: () => fs.existsSync('Cargo.toml'),
217
+ go: () => fs.existsSync('go.mod')
218
+ };
219
+
220
+ // Install commands for each project type
221
+ const installers = {
222
+ nodejs: () => {
223
+ try {
224
+ execSync(
225
+ process.platform === 'win32'
226
+ ? `where bun`
227
+ : `command -v bun`,
228
+ { stdio: 'ignore' }
229
+ );
230
+ exec('bun install');
231
+ exec('bun run dev; bun run start');
232
+ } catch (error) {
233
+ exec('npm install');
234
+ exec('npm run dev; npm run start');
235
+ }
236
+ },
237
+ docker: () => {
238
+ if (fs.existsSync('docker-compose.yml')) {
239
+ exec('sudo docker-compose up -d');
240
+ } else if (fs.existsSync('Dockerfile')) {
241
+ exec('sudo docker build -t project .');
242
+ }
243
+ },
244
+ python: () => {
245
+ exec('python -m venv .venv');
246
+ exec('source .venv/bin/activate');
247
+
248
+ if (fs.existsSync('requirements.txt')) {
249
+ exec('pip install -r requirements.txt');
250
+ }
251
+ if (fs.existsSync('setup.py')) {
252
+ exec('pip install -e .');
253
+ }
254
+ },
255
+ rust: () => exec('cargo build'),
256
+ go: () => exec('go mod tidy')
257
+ };
258
+
259
+ // Run detections and installations
260
+ Object.entries(detectors).forEach(([name, check]) => {
261
+ if (check()) {
262
+ // log(chalk.yellow(`⚙️ Detected ${name} project`));
263
+ installers[name]?.();
264
+ }
265
+ });
266
+ }
267
+
268
+ /**
269
+ * Executes a shell command with error handling
270
+ * @param {string} cmd - Command to execute
271
+ * @param {boolean} [showError=false] - Whether to display error messages
272
+ */
273
+ function exec(cmd, showError = false) {
274
+ try {
275
+ execSync(cmd, { stdio: 'inherit' });
276
+ } catch (error) {
277
+ if (showError)
278
+ console.error(chalk.red(`❌ Failed: ${cmd}`));
279
+ }
280
+ }
281
+
282
+ /**
283
+ * Downloads a GitHub repository and sets it up for development
284
+ * Opens the project in an IDE and installs dependencies automatically
285
+ * @async
286
+ * @param {string} repo - Repository URL or identifier
287
+ * @param {string|null} [folderPath=null] - Optional custom folder path for extraction
288
+ */
289
+ async function downloadRepoAndSetup(repo, folderPath = null) {
290
+
291
+ printLogo()
292
+
293
+ var extractPath = await Github.downloadRepo(repo, folderPath)
294
+
295
+ setTimeout(() => {
296
+ openInIDE(extractPath);
297
+ }, 500);
298
+ installDependencies(extractPath);
299
+ }
300
+
301
+ /**
302
+ * Main CLI function that handles argument parsing and repository selection
303
+ * Supports both download of direct GitHub URLs and search queries
304
+ * @async
305
+ * @throws {Error} When no search query is provided or no repositories are found
306
+ */
307
+ async function main() {
308
+
309
+ printLogo()
310
+ const args = process.argv.slice(2);
311
+ if (!args.length) {
312
+ log(chalk.yellow('Usage: git0 <search-query>'));
313
+ process.exit(1);
314
+ }
315
+
316
+ const query = args.join(' ');
317
+ // Try parsing as a GitHub URL or shorthand
318
+ let parsed = Github.parseURL(query)
319
+ if (parsed && parsed.owner && parsed.name) {
320
+ if (typeof args[1] !== 'undefined')
321
+ await downloadRepoAndSetup(parsed.href, args[1]);
322
+ else
323
+ await downloadRepoAndSetup(parsed.href);
324
+
325
+ return;
326
+ }
327
+
328
+ const results = await Github.searchRepositories(query);
329
+
330
+ if (!results || !results.length) {
331
+ log(chalk.yellow('No repositories found'));
332
+ process.exit(1);
333
+ }
334
+
335
+ const { selectedRepo } = await inquirer.prompt({
336
+ type: 'list',
337
+ name: 'selectedRepo',
338
+ message: 'Select a repository to download:',
339
+ choices: results.map(repo => {
340
+ const packageInfo = repo.hasCompatibleReleases
341
+ ? chalk.green(' 📦 Packages available')
342
+ : repo.hasReleases
343
+ ? chalk.yellow(' 📦 Packages (other platforms)')
344
+ : '';
345
+
346
+ return {
347
+ name: `${chalk.bold(repo.full_name)} - ${chalk.gray(repo.description || 'No description')}
348
+ ${chalk.yellow(`★ ${repo.stargazers_count}`)} | ${chalk.blue(repo.language || 'Unknown')}${packageInfo}`,
349
+ value: repo
350
+ };
351
+ })
352
+ });
353
+
354
+ // If the selected repo has any releases, show download options
355
+ if (selectedRepo.hasReleases || selectedRepo.hasCompatibleReleases) {
356
+ const { downloadChoice } = await inquirer.prompt({
357
+ type: 'list',
358
+ name: 'downloadChoice',
359
+ message: 'This repository has downloadable packages. What would you like to do?',
360
+ choices: [
361
+ { name: '📦 Download package/binary', value: 'package' },
362
+ { name: '📂 Download source code', value: 'source' },
363
+ { name: '📦📂 Download both package and source', value: 'both' }
364
+ ]
365
+ });
366
+
367
+ if (downloadChoice === 'package' || downloadChoice === 'both') {
368
+ await showPackageMenu(selectedRepo);
369
+ }
370
+
371
+ if (downloadChoice === 'source' || downloadChoice === 'both') {
372
+ await downloadRepoAndSetup(selectedRepo.url);
373
+ }
374
+ } else {
375
+ // No packages, just download source
376
+ await downloadRepoAndSetup(selectedRepo.url);
377
+ }
378
+ }
379
+
380
+ main();