@fredlackey/devutils 0.0.1 → 0.0.3

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 (259) hide show
  1. package/README.md +5 -5
  2. package/package.json +1 -1
  3. package/src/commands/install.js +374 -36
  4. package/src/installs/adobe-creative-cloud.js +527 -25
  5. package/src/installs/adobe-creative-cloud.md +605 -0
  6. package/src/installs/appcleaner.js +303 -26
  7. package/src/installs/appcleaner.md +699 -0
  8. package/src/installs/apt-transport-https.js +390 -0
  9. package/src/installs/apt-transport-https.md +678 -0
  10. package/src/installs/atomicparsley.js +624 -26
  11. package/src/installs/atomicparsley.md +795 -0
  12. package/src/installs/aws-cli.js +779 -26
  13. package/src/installs/aws-cli.md +727 -0
  14. package/src/installs/balena-etcher.js +688 -26
  15. package/src/installs/balena-etcher.md +761 -0
  16. package/src/installs/bambu-studio.js +912 -26
  17. package/src/installs/bambu-studio.md +780 -0
  18. package/src/installs/bash-completion.js +554 -23
  19. package/src/installs/bash-completion.md +833 -0
  20. package/src/installs/bash.js +399 -26
  21. package/src/installs/bash.md +993 -0
  22. package/src/installs/beyond-compare.js +585 -26
  23. package/src/installs/beyond-compare.md +813 -0
  24. package/src/installs/build-essential.js +511 -26
  25. package/src/installs/build-essential.md +977 -0
  26. package/src/installs/ca-certificates.js +618 -0
  27. package/src/installs/ca-certificates.md +937 -0
  28. package/src/installs/caffeine.js +490 -26
  29. package/src/installs/caffeine.md +839 -0
  30. package/src/installs/camtasia.js +577 -25
  31. package/src/installs/camtasia.md +762 -0
  32. package/src/installs/chatgpt.js +458 -26
  33. package/src/installs/chatgpt.md +814 -0
  34. package/src/installs/chocolatey.js +447 -0
  35. package/src/installs/chocolatey.md +661 -0
  36. package/src/installs/chrome-canary.js +472 -26
  37. package/src/installs/chrome-canary.md +641 -0
  38. package/src/installs/chromium.js +645 -26
  39. package/src/installs/chromium.md +838 -0
  40. package/src/installs/claude-code.js +558 -26
  41. package/src/installs/claude-code.md +1173 -0
  42. package/src/installs/curl.js +361 -26
  43. package/src/installs/curl.md +714 -0
  44. package/src/installs/cursor.js +561 -26
  45. package/src/installs/cursor.md +970 -0
  46. package/src/installs/dbschema.js +674 -26
  47. package/src/installs/dbschema.md +925 -0
  48. package/src/installs/dependencies.md +435 -0
  49. package/src/installs/development-tools.js +600 -0
  50. package/src/installs/development-tools.md +977 -0
  51. package/src/installs/docker.js +1010 -25
  52. package/src/installs/docker.md +1109 -0
  53. package/src/installs/drawio.js +1001 -26
  54. package/src/installs/drawio.md +795 -0
  55. package/src/installs/elmedia-player.js +328 -25
  56. package/src/installs/elmedia-player.md +556 -0
  57. package/src/installs/ffmpeg.js +870 -25
  58. package/src/installs/ffmpeg.md +852 -0
  59. package/src/installs/file.js +464 -0
  60. package/src/installs/file.md +987 -0
  61. package/src/installs/gemini-cli.js +793 -26
  62. package/src/installs/gemini-cli.md +1153 -0
  63. package/src/installs/git.js +382 -26
  64. package/src/installs/git.md +907 -0
  65. package/src/installs/gitego.js +931 -26
  66. package/src/installs/gitego.md +1172 -0
  67. package/src/installs/go.js +913 -26
  68. package/src/installs/go.md +958 -0
  69. package/src/installs/google-chrome.js +801 -25
  70. package/src/installs/google-chrome.md +862 -0
  71. package/src/installs/gpg.js +412 -73
  72. package/src/installs/gpg.md +1056 -0
  73. package/src/installs/homebrew.js +1015 -26
  74. package/src/installs/homebrew.md +988 -0
  75. package/src/installs/imageoptim.js +950 -26
  76. package/src/installs/imageoptim.md +1119 -0
  77. package/src/installs/installers.json +2297 -0
  78. package/src/installs/jq.js +382 -26
  79. package/src/installs/jq.md +809 -0
  80. package/src/installs/keyboard-maestro.js +701 -26
  81. package/src/installs/keyboard-maestro.md +825 -0
  82. package/src/installs/latex.js +771 -26
  83. package/src/installs/latex.md +1095 -0
  84. package/src/installs/lftp.js +338 -26
  85. package/src/installs/lftp.md +907 -0
  86. package/src/installs/lsb-release.js +346 -0
  87. package/src/installs/lsb-release.md +814 -0
  88. package/src/installs/messenger.js +829 -26
  89. package/src/installs/messenger.md +900 -0
  90. package/src/installs/microsoft-office.js +550 -26
  91. package/src/installs/microsoft-office.md +760 -0
  92. package/src/installs/microsoft-teams.js +782 -25
  93. package/src/installs/microsoft-teams.md +886 -0
  94. package/src/installs/node.js +886 -26
  95. package/src/installs/node.md +1153 -0
  96. package/src/installs/nordpass.js +698 -26
  97. package/src/installs/nordpass.md +921 -0
  98. package/src/installs/nvm.js +977 -26
  99. package/src/installs/nvm.md +1057 -0
  100. package/src/installs/openssh.js +734 -64
  101. package/src/installs/openssh.md +1056 -0
  102. package/src/installs/pandoc.js +644 -26
  103. package/src/installs/pandoc.md +1036 -0
  104. package/src/installs/pinentry.js +492 -26
  105. package/src/installs/pinentry.md +1142 -0
  106. package/src/installs/pngyu.js +851 -26
  107. package/src/installs/pngyu.md +896 -0
  108. package/src/installs/postman.js +781 -26
  109. package/src/installs/postman.md +940 -0
  110. package/src/installs/procps.js +425 -0
  111. package/src/installs/procps.md +851 -0
  112. package/src/installs/safari-tech-preview.js +355 -25
  113. package/src/installs/safari-tech-preview.md +533 -0
  114. package/src/installs/sfnt2woff.js +640 -26
  115. package/src/installs/sfnt2woff.md +795 -0
  116. package/src/installs/shellcheck.js +463 -26
  117. package/src/installs/shellcheck.md +1005 -0
  118. package/src/installs/slack.js +722 -25
  119. package/src/installs/slack.md +865 -0
  120. package/src/installs/snagit.js +566 -25
  121. package/src/installs/snagit.md +844 -0
  122. package/src/installs/software-properties-common.js +372 -0
  123. package/src/installs/software-properties-common.md +805 -0
  124. package/src/installs/spotify.js +858 -25
  125. package/src/installs/spotify.md +901 -0
  126. package/src/installs/studio-3t.js +803 -26
  127. package/src/installs/studio-3t.md +918 -0
  128. package/src/installs/sublime-text.js +780 -25
  129. package/src/installs/sublime-text.md +914 -0
  130. package/src/installs/superwhisper.js +687 -25
  131. package/src/installs/superwhisper.md +630 -0
  132. package/src/installs/tailscale.js +727 -26
  133. package/src/installs/tailscale.md +1100 -0
  134. package/src/installs/tar.js +389 -0
  135. package/src/installs/tar.md +946 -0
  136. package/src/installs/termius.js +780 -26
  137. package/src/installs/termius.md +844 -0
  138. package/src/installs/terraform.js +761 -26
  139. package/src/installs/terraform.md +899 -0
  140. package/src/installs/tidal.js +752 -25
  141. package/src/installs/tidal.md +864 -0
  142. package/src/installs/tmux.js +328 -26
  143. package/src/installs/tmux.md +1030 -0
  144. package/src/installs/tree.js +393 -26
  145. package/src/installs/tree.md +833 -0
  146. package/src/installs/unzip.js +460 -0
  147. package/src/installs/unzip.md +879 -0
  148. package/src/installs/vim.js +403 -26
  149. package/src/installs/vim.md +1040 -0
  150. package/src/installs/vlc.js +803 -26
  151. package/src/installs/vlc.md +927 -0
  152. package/src/installs/vscode.js +825 -26
  153. package/src/installs/vscode.md +1002 -0
  154. package/src/installs/wget.js +415 -0
  155. package/src/installs/wget.md +791 -0
  156. package/src/installs/whatsapp.js +710 -25
  157. package/src/installs/whatsapp.md +854 -0
  158. package/src/installs/winpty.js +352 -0
  159. package/src/installs/winpty.md +620 -0
  160. package/src/installs/woff2.js +535 -26
  161. package/src/installs/woff2.md +977 -0
  162. package/src/installs/wsl.js +572 -0
  163. package/src/installs/wsl.md +699 -0
  164. package/src/installs/xcode-clt.js +520 -0
  165. package/src/installs/xcode-clt.md +351 -0
  166. package/src/installs/xcode.js +542 -26
  167. package/src/installs/xcode.md +573 -0
  168. package/src/installs/yarn.js +806 -26
  169. package/src/installs/yarn.md +1074 -0
  170. package/src/installs/yq.js +636 -26
  171. package/src/installs/yq.md +944 -0
  172. package/src/installs/yt-dlp.js +683 -26
  173. package/src/installs/yt-dlp.md +946 -0
  174. package/src/installs/yum-utils.js +297 -0
  175. package/src/installs/yum-utils.md +648 -0
  176. package/src/installs/zoom.js +740 -25
  177. package/src/installs/zoom.md +884 -0
  178. package/src/scripts/README.md +567 -45
  179. package/src/scripts/STATUS.md +208 -0
  180. package/src/scripts/afk.js +395 -7
  181. package/src/scripts/backup-all.js +731 -9
  182. package/src/scripts/backup-source.js +711 -8
  183. package/src/scripts/brewd.js +373 -7
  184. package/src/scripts/brewi.js +505 -9
  185. package/src/scripts/brewr.js +512 -9
  186. package/src/scripts/brews.js +462 -9
  187. package/src/scripts/brewu.js +488 -7
  188. package/src/scripts/c.js +185 -7
  189. package/src/scripts/ccurl.js +325 -8
  190. package/src/scripts/certbot-crontab-init.js +488 -8
  191. package/src/scripts/certbot-init.js +641 -9
  192. package/src/scripts/ch.js +339 -7
  193. package/src/scripts/claude-danger.js +253 -8
  194. package/src/scripts/clean-dev.js +419 -8
  195. package/src/scripts/clear-dns-cache.js +525 -7
  196. package/src/scripts/clone.js +417 -7
  197. package/src/scripts/code-all.js +420 -7
  198. package/src/scripts/count-files.js +195 -8
  199. package/src/scripts/count-folders.js +195 -8
  200. package/src/scripts/count.js +248 -8
  201. package/src/scripts/d.js +203 -7
  202. package/src/scripts/datauri.js +373 -8
  203. package/src/scripts/delete-files.js +363 -7
  204. package/src/scripts/docker-clean.js +410 -8
  205. package/src/scripts/dp.js +426 -7
  206. package/src/scripts/e.js +375 -9
  207. package/src/scripts/empty-trash.js +497 -7
  208. package/src/scripts/evm.js +428 -9
  209. package/src/scripts/fetch-github-repos.js +441 -10
  210. package/src/scripts/get-channel.js +329 -8
  211. package/src/scripts/get-course.js +384 -11
  212. package/src/scripts/get-dependencies.js +290 -9
  213. package/src/scripts/get-folder.js +783 -10
  214. package/src/scripts/get-tunes.js +411 -10
  215. package/src/scripts/get-video.js +352 -9
  216. package/src/scripts/git-backup.js +561 -9
  217. package/src/scripts/git-clone.js +477 -9
  218. package/src/scripts/git-pup.js +303 -7
  219. package/src/scripts/git-push.js +380 -8
  220. package/src/scripts/h.js +607 -9
  221. package/src/scripts/hide-desktop-icons.js +483 -7
  222. package/src/scripts/hide-hidden-files.js +522 -7
  223. package/src/scripts/install-dependencies-from.js +440 -9
  224. package/src/scripts/ips.js +647 -10
  225. package/src/scripts/iso.js +354 -8
  226. package/src/scripts/killni.js +561 -7
  227. package/src/scripts/ll.js +451 -8
  228. package/src/scripts/local-ip.js +310 -8
  229. package/src/scripts/m.js +508 -8
  230. package/src/scripts/map.js +293 -8
  231. package/src/scripts/mkd.js +287 -7
  232. package/src/scripts/ncu-update-all.js +441 -8
  233. package/src/scripts/nginx-init.js +702 -12
  234. package/src/scripts/npmi.js +366 -7
  235. package/src/scripts/o.js +495 -8
  236. package/src/scripts/org-by-date.js +321 -7
  237. package/src/scripts/p.js +208 -7
  238. package/src/scripts/packages.js +313 -8
  239. package/src/scripts/path.js +209 -7
  240. package/src/scripts/ports.js +582 -8
  241. package/src/scripts/q.js +290 -8
  242. package/src/scripts/refresh-files.js +378 -10
  243. package/src/scripts/remove-smaller-files.js +500 -8
  244. package/src/scripts/rename-files-with-date.js +517 -9
  245. package/src/scripts/resize-image.js +523 -9
  246. package/src/scripts/rm-safe.js +653 -8
  247. package/src/scripts/s.js +525 -9
  248. package/src/scripts/set-git-public.js +349 -7
  249. package/src/scripts/show-desktop-icons.js +459 -7
  250. package/src/scripts/show-hidden-files.js +456 -7
  251. package/src/scripts/tpa.js +265 -8
  252. package/src/scripts/tpo.js +264 -7
  253. package/src/scripts/u.js +489 -7
  254. package/src/scripts/vpush.js +422 -8
  255. package/src/scripts/y.js +267 -7
  256. package/src/utils/common/os.js +94 -2
  257. package/src/utils/ubuntu/apt.js +13 -7
  258. package/src/utils/windows/choco.js +82 -26
  259. package/src/utils/windows/winget.js +89 -27
@@ -1,25 +1,456 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * @fileoverview Clone all repositories from a GitHub organization.
4
+ * fetch-github-repos - Clone all repositories from a GitHub organization
5
+ *
6
+ * Migrated from legacy dotfiles function.
7
+ * Original:
8
+ * fetch-github-repos() {
9
+ * local org="$1"
10
+ * local dest_dir="$2"
11
+ * if [[ -z "$org" || -z "$dest_dir" ]]; then
12
+ * echo "Usage: fetch-github-repos <organization> <destination-folder>"
13
+ * return 1
14
+ * fi
15
+ * if ! command -v jq >/dev/null 2>&1; then
16
+ * echo "Error: 'jq' is required but not installed."
17
+ * return 1
18
+ * fi
19
+ * mkdir -p "$dest_dir"
20
+ * echo "Fetching repositories for organization '$org'..."
21
+ * local repos=$(curl -s "https://api.github.com/orgs/$org/repos?per_page=100" | jq -r '.[].ssh_url')
22
+ * if [[ -z "$repos" ]]; then
23
+ * echo "No repositories found or failed to fetch from GitHub."
24
+ * return 1
25
+ * fi
26
+ * for repo in $repos; do
27
+ * echo "Cloning $repo into $dest_dir..."
28
+ * git clone "$repo" "$dest_dir/$(basename -s .git "$repo")"
29
+ * done
30
+ * echo "All repositories have been cloned."
31
+ * }
32
+ *
33
+ * This script fetches the list of repositories from a GitHub organization
34
+ * using the GitHub API and clones each one into a specified destination folder.
35
+ * Unlike the original bash version, this uses Node.js native fetch() instead of
36
+ * curl+jq, making it cross-platform without additional dependencies.
37
+ *
5
38
  * @module scripts/fetch-github-repos
6
39
  */
7
40
 
41
+ const os = require('../utils/common/os');
42
+ const fs = require('fs');
43
+ const path = require('path');
44
+ const { execSync, spawnSync } = require('child_process');
45
+
46
+ /**
47
+ * Helper function to check if a command exists on the system.
48
+ * Used to verify git is available before attempting to clone.
49
+ *
50
+ * @param {string} cmd - The command name to check
51
+ * @returns {boolean} True if the command exists, false otherwise
52
+ */
53
+ function isCommandAvailable(cmd) {
54
+ try {
55
+ // Use 'which' on Unix-like systems, 'where' on Windows
56
+ const checkCmd = process.platform === 'win32' ? `where ${cmd}` : `which ${cmd}`;
57
+ execSync(checkCmd, { stdio: 'ignore' });
58
+ return true;
59
+ } catch {
60
+ return false;
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Fetches the list of repositories from a GitHub organization.
66
+ *
67
+ * Uses the GitHub API to get all public repositories for an organization.
68
+ * The API returns up to 100 repositories per page. For organizations with
69
+ * more than 100 repos, pagination would be needed (not implemented here
70
+ * to match original behavior).
71
+ *
72
+ * @param {string} org - The GitHub organization name
73
+ * @returns {Promise<Array<{name: string, ssh_url: string, clone_url: string}>>} Array of repo objects
74
+ * @throws {Error} If the API request fails
75
+ */
76
+ async function fetchRepositories(org) {
77
+ // Use Node.js native fetch() - available in Node.js 18+
78
+ // This replaces the original curl + jq approach
79
+ const apiUrl = `https://api.github.com/orgs/${org}/repos?per_page=100`;
80
+
81
+ const response = await fetch(apiUrl, {
82
+ headers: {
83
+ // GitHub recommends setting a User-Agent header
84
+ 'User-Agent': 'devutils-cli',
85
+ 'Accept': 'application/vnd.github.v3+json'
86
+ }
87
+ });
88
+
89
+ if (!response.ok) {
90
+ if (response.status === 404) {
91
+ throw new Error(`Organization '${org}' not found on GitHub.`);
92
+ }
93
+ if (response.status === 403) {
94
+ throw new Error(
95
+ 'GitHub API rate limit exceeded. ' +
96
+ 'Try again later or use a GitHub token for higher limits.'
97
+ );
98
+ }
99
+ throw new Error(`GitHub API error: ${response.status} ${response.statusText}`);
100
+ }
101
+
102
+ // Parse JSON response - this replaces the jq parsing in the original
103
+ const repos = await response.json();
104
+
105
+ if (!Array.isArray(repos)) {
106
+ throw new Error('Unexpected response format from GitHub API.');
107
+ }
108
+
109
+ return repos;
110
+ }
111
+
8
112
  /**
9
- * Fetches the list of repositories from a GitHub organization
10
- * and clones each one into the specified destination folder.
113
+ * Clones a single repository into the destination directory.
114
+ *
115
+ * Uses git clone to download the repository. The repository is cloned
116
+ * using the SSH URL by default (matching original behavior), but falls
117
+ * back to HTTPS if SSH fails.
118
+ *
119
+ * @param {string} repoUrl - The SSH or HTTPS URL of the repository
120
+ * @param {string} destPath - The full path where the repo should be cloned
121
+ * @returns {boolean} True if clone succeeded, false otherwise
122
+ */
123
+ function cloneRepository(repoUrl, destPath) {
124
+ // Check if destination already exists (idempotency)
125
+ if (fs.existsSync(destPath)) {
126
+ console.log(` Skipping: ${path.basename(destPath)} (already exists)`);
127
+ return true;
128
+ }
129
+
130
+ try {
131
+ // Use spawnSync for better output control
132
+ const result = spawnSync('git', ['clone', repoUrl, destPath], {
133
+ stdio: 'inherit',
134
+ encoding: 'utf8'
135
+ });
136
+
137
+ if (result.status !== 0) {
138
+ console.error(` Failed to clone: ${path.basename(destPath)}`);
139
+ return false;
140
+ }
141
+
142
+ return true;
143
+ } catch (error) {
144
+ console.error(` Error cloning ${path.basename(destPath)}: ${error.message}`);
145
+ return false;
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Pure Node.js implementation for fetching and cloning GitHub organization repos.
151
+ *
152
+ * This function uses pure Node.js for:
153
+ * - HTTP requests (native fetch())
154
+ * - JSON parsing (JSON.parse via response.json())
155
+ * - Directory creation (fs.mkdirSync)
156
+ * - Path manipulation (path.join, path.basename)
157
+ *
158
+ * The only external command required is 'git' for cloning, which is
159
+ * necessary because reimplementing git clone in Node.js would be
160
+ * impractical and inferior to the native tool.
161
+ *
162
+ * @param {string[]} args - Command line arguments [org, destDir]
163
+ * @returns {Promise<void>}
164
+ */
165
+ async function do_fetch_github_repos_nodejs(args) {
166
+ const org = args[0];
167
+ const destDir = args[1];
168
+
169
+ // Validate arguments
170
+ if (!org || !destDir) {
171
+ console.log('Usage: fetch-github-repos <organization> <destination-folder>');
172
+ console.log('');
173
+ console.log('Arguments:');
174
+ console.log(' organization The GitHub organization name');
175
+ console.log(' destination-folder The folder where repos will be cloned');
176
+ console.log('');
177
+ console.log('Example:');
178
+ console.log(' fetch-github-repos nodejs ./nodejs-repos');
179
+ process.exit(1);
180
+ }
181
+
182
+ // Check if git is available (required for cloning)
183
+ if (!isCommandAvailable('git')) {
184
+ console.error('Error: git is required but not installed.');
185
+ console.error('');
186
+ console.error('Install git:');
187
+ console.error(' macOS: brew install git');
188
+ console.error(' Ubuntu: sudo apt install git');
189
+ console.error(' Windows: https://git-scm.com/download/win');
190
+ process.exit(1);
191
+ }
192
+
193
+ // Resolve the destination directory to an absolute path
194
+ const absoluteDestDir = path.resolve(destDir);
195
+
196
+ // Create destination directory if it doesn't exist (pure Node.js)
197
+ // Using recursive: true makes this idempotent - it won't fail if dir exists
198
+ if (!fs.existsSync(absoluteDestDir)) {
199
+ fs.mkdirSync(absoluteDestDir, { recursive: true });
200
+ console.log(`Created directory: ${absoluteDestDir}`);
201
+ }
202
+
203
+ console.log(`Fetching repositories for organization '${org}'...`);
204
+ console.log('');
205
+
206
+ // Fetch the list of repositories from GitHub API
207
+ let repos;
208
+ try {
209
+ repos = await fetchRepositories(org);
210
+ } catch (error) {
211
+ console.error(`Error: ${error.message}`);
212
+ process.exit(1);
213
+ }
214
+
215
+ // Check if any repositories were found
216
+ if (repos.length === 0) {
217
+ console.log('No repositories found for this organization.');
218
+ console.log('');
219
+ console.log('This could mean:');
220
+ console.log(' - The organization has no public repositories');
221
+ console.log(' - The organization name is incorrect');
222
+ console.log(' - Private repositories require authentication');
223
+ return;
224
+ }
225
+
226
+ console.log(`Found ${repos.length} repositories.`);
227
+ console.log('');
228
+
229
+ // Clone each repository
230
+ let successCount = 0;
231
+ let skipCount = 0;
232
+ let failCount = 0;
233
+
234
+ for (const repo of repos) {
235
+ // Extract repository name from the SSH URL (e.g., "git@github.com:org/repo.git" -> "repo")
236
+ const repoName = repo.name || path.basename(repo.ssh_url, '.git');
237
+ const destPath = path.join(absoluteDestDir, repoName);
238
+
239
+ // Check if already cloned (idempotency)
240
+ if (fs.existsSync(destPath)) {
241
+ console.log(`Skipping: ${repoName} (already exists)`);
242
+ skipCount++;
243
+ continue;
244
+ }
245
+
246
+ console.log(`Cloning: ${repoName}...`);
247
+
248
+ // Try SSH URL first (original behavior), fall back to HTTPS
249
+ const sshUrl = repo.ssh_url;
250
+ const httpsUrl = repo.clone_url;
251
+
252
+ let cloneSuccess = cloneRepository(sshUrl, destPath);
253
+
254
+ // If SSH fails, try HTTPS
255
+ if (!cloneSuccess && httpsUrl) {
256
+ console.log(` SSH clone failed, trying HTTPS...`);
257
+ cloneSuccess = cloneRepository(httpsUrl, destPath);
258
+ }
259
+
260
+ if (cloneSuccess) {
261
+ successCount++;
262
+ } else {
263
+ failCount++;
264
+ }
265
+ }
266
+
267
+ // Print summary
268
+ console.log('');
269
+ console.log('--- Summary ---');
270
+ console.log(`Successfully cloned: ${successCount}`);
271
+ if (skipCount > 0) {
272
+ console.log(`Already existed (skipped): ${skipCount}`);
273
+ }
274
+ if (failCount > 0) {
275
+ console.log(`Failed to clone: ${failCount}`);
276
+ }
277
+ console.log(`Total repositories: ${repos.length}`);
278
+ console.log(`Destination: ${absoluteDestDir}`);
279
+ }
280
+
281
+ /**
282
+ * Clone all repositories from a GitHub organization on macOS.
283
+ *
284
+ * Uses the pure Node.js implementation since all required functionality
285
+ * (HTTP requests, JSON parsing, file operations) works identically on macOS.
286
+ * The only external dependency is git, which is required on all platforms.
11
287
  *
12
- * @param {string[]} args - Command line arguments
13
- * @param {string} args.0 - GitHub organization name
14
- * @param {string} args.1 - Destination folder path
288
+ * @param {string[]} args - Command line arguments [org, destDir]
15
289
  * @returns {Promise<void>}
16
290
  */
17
- async function main(args) {
18
- // TODO: Implement GitHub org repo fetcher
291
+ async function do_fetch_github_repos_macos(args) {
292
+ // macOS can use the pure Node.js implementation
293
+ return do_fetch_github_repos_nodejs(args);
294
+ }
295
+
296
+ /**
297
+ * Clone all repositories from a GitHub organization on Ubuntu.
298
+ *
299
+ * Uses the pure Node.js implementation since all required functionality
300
+ * (HTTP requests, JSON parsing, file operations) works identically on Ubuntu.
301
+ * The only external dependency is git, which is required on all platforms.
302
+ *
303
+ * @param {string[]} args - Command line arguments [org, destDir]
304
+ * @returns {Promise<void>}
305
+ */
306
+ async function do_fetch_github_repos_ubuntu(args) {
307
+ // Ubuntu can use the pure Node.js implementation
308
+ return do_fetch_github_repos_nodejs(args);
309
+ }
310
+
311
+ /**
312
+ * Clone all repositories from a GitHub organization on Raspberry Pi OS.
313
+ *
314
+ * Uses the pure Node.js implementation since all required functionality
315
+ * (HTTP requests, JSON parsing, file operations) works identically on Raspbian.
316
+ * The only external dependency is git, which is required on all platforms.
317
+ *
318
+ * @param {string[]} args - Command line arguments [org, destDir]
319
+ * @returns {Promise<void>}
320
+ */
321
+ async function do_fetch_github_repos_raspbian(args) {
322
+ // Raspbian can use the pure Node.js implementation
323
+ return do_fetch_github_repos_nodejs(args);
324
+ }
325
+
326
+ /**
327
+ * Clone all repositories from a GitHub organization on Amazon Linux.
328
+ *
329
+ * Uses the pure Node.js implementation since all required functionality
330
+ * (HTTP requests, JSON parsing, file operations) works identically on Amazon Linux.
331
+ * The only external dependency is git, which is required on all platforms.
332
+ *
333
+ * @param {string[]} args - Command line arguments [org, destDir]
334
+ * @returns {Promise<void>}
335
+ */
336
+ async function do_fetch_github_repos_amazon_linux(args) {
337
+ // Amazon Linux can use the pure Node.js implementation
338
+ return do_fetch_github_repos_nodejs(args);
339
+ }
340
+
341
+ /**
342
+ * Clone all repositories from a GitHub organization on Windows Command Prompt.
343
+ *
344
+ * Uses the pure Node.js implementation since all required functionality
345
+ * (HTTP requests, JSON parsing, file operations) works identically on Windows.
346
+ * The only external dependency is git, which must be installed and in PATH.
347
+ *
348
+ * @param {string[]} args - Command line arguments [org, destDir]
349
+ * @returns {Promise<void>}
350
+ */
351
+ async function do_fetch_github_repos_cmd(args) {
352
+ // Windows CMD can use the pure Node.js implementation
353
+ return do_fetch_github_repos_nodejs(args);
354
+ }
355
+
356
+ /**
357
+ * Clone all repositories from a GitHub organization on Windows PowerShell.
358
+ *
359
+ * Uses the pure Node.js implementation since all required functionality
360
+ * (HTTP requests, JSON parsing, file operations) works identically on Windows.
361
+ * The only external dependency is git, which must be installed and in PATH.
362
+ *
363
+ * @param {string[]} args - Command line arguments [org, destDir]
364
+ * @returns {Promise<void>}
365
+ */
366
+ async function do_fetch_github_repos_powershell(args) {
367
+ // Windows PowerShell can use the pure Node.js implementation
368
+ return do_fetch_github_repos_nodejs(args);
369
+ }
370
+
371
+ /**
372
+ * Clone all repositories from a GitHub organization on Git Bash.
373
+ *
374
+ * Uses the pure Node.js implementation since all required functionality
375
+ * (HTTP requests, JSON parsing, file operations) works identically on Git Bash.
376
+ * Git is guaranteed to be available since we're running in Git Bash.
377
+ *
378
+ * @param {string[]} args - Command line arguments [org, destDir]
379
+ * @returns {Promise<void>}
380
+ */
381
+ async function do_fetch_github_repos_gitbash(args) {
382
+ // Git Bash can use the pure Node.js implementation
383
+ // Bonus: git is guaranteed to be available in Git Bash
384
+ return do_fetch_github_repos_nodejs(args);
385
+ }
386
+
387
+ /**
388
+ * Main entry point - detects environment and executes appropriate implementation.
389
+ *
390
+ * This script clones all repositories from a GitHub organization into a
391
+ * destination folder. It's useful for:
392
+ * - Backing up all repos from an organization
393
+ * - Setting up a development environment with multiple related repos
394
+ * - Migrating repositories between systems
395
+ *
396
+ * The script is idempotent: running it multiple times will skip repos that
397
+ * have already been cloned, making it safe to re-run to catch new repos.
398
+ *
399
+ * Unlike the original bash version that required curl and jq, this Node.js
400
+ * implementation uses native fetch() and JSON.parse(), requiring only git
401
+ * as an external dependency.
402
+ *
403
+ * @param {string[]} args - Command line arguments [org, destDir]
404
+ * @returns {Promise<void>}
405
+ */
406
+ async function do_fetch_github_repos(args) {
407
+ const platform = os.detect();
408
+
409
+ const handlers = {
410
+ 'macos': do_fetch_github_repos_macos,
411
+ 'ubuntu': do_fetch_github_repos_ubuntu,
412
+ 'debian': do_fetch_github_repos_ubuntu,
413
+ 'raspbian': do_fetch_github_repos_raspbian,
414
+ 'amazon_linux': do_fetch_github_repos_amazon_linux,
415
+ 'rhel': do_fetch_github_repos_amazon_linux,
416
+ 'fedora': do_fetch_github_repos_ubuntu,
417
+ 'linux': do_fetch_github_repos_ubuntu,
418
+ 'wsl': do_fetch_github_repos_ubuntu,
419
+ 'cmd': do_fetch_github_repos_cmd,
420
+ 'windows': do_fetch_github_repos_cmd,
421
+ 'powershell': do_fetch_github_repos_powershell,
422
+ 'gitbash': do_fetch_github_repos_gitbash
423
+ };
424
+
425
+ const handler = handlers[platform.type];
426
+ if (!handler) {
427
+ console.error(`Platform '${platform.type}' is not supported for this command.`);
428
+ console.error('');
429
+ console.error('Supported platforms:');
430
+ console.error(' - macOS');
431
+ console.error(' - Ubuntu, Debian, and other Linux distributions');
432
+ console.error(' - Raspberry Pi OS');
433
+ console.error(' - Amazon Linux, RHEL, Fedora');
434
+ console.error(' - Windows (CMD, PowerShell, Git Bash)');
435
+ process.exit(1);
436
+ }
437
+
438
+ await handler(args);
19
439
  }
20
440
 
21
- module.exports = { main };
441
+ module.exports = {
442
+ main: do_fetch_github_repos,
443
+ do_fetch_github_repos,
444
+ do_fetch_github_repos_nodejs,
445
+ do_fetch_github_repos_macos,
446
+ do_fetch_github_repos_ubuntu,
447
+ do_fetch_github_repos_raspbian,
448
+ do_fetch_github_repos_amazon_linux,
449
+ do_fetch_github_repos_cmd,
450
+ do_fetch_github_repos_powershell,
451
+ do_fetch_github_repos_gitbash
452
+ };
22
453
 
23
454
  if (require.main === module) {
24
- main(process.argv.slice(2));
455
+ do_fetch_github_repos(process.argv.slice(2));
25
456
  }