@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,493 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * @fileoverview Copy repository structure without .git folder.
4
+ * git-clone - Copy repository structure without .git folder and common excludes
5
+ *
6
+ * Migrated from legacy dotfiles function.
7
+ * Original:
8
+ * git-clone(){
9
+ * eval "rsync -av --progress $* ./ --exclude .git --exclude README.md --exclude LICENSE --exclude node_modules --exclude bower_components"
10
+ * }
11
+ *
12
+ * This script copies files from a source directory (typically a git repository)
13
+ * to the current directory, excluding common files and folders that should not
14
+ * be copied when using a repo as a template:
15
+ * - .git (version control history)
16
+ * - README.md (documentation specific to the source)
17
+ * - LICENSE (license specific to the source)
18
+ * - node_modules (dependencies that should be reinstalled)
19
+ * - bower_components (legacy dependencies that should be reinstalled)
20
+ *
21
+ * Use case: You want to use an existing repository as a starting template for
22
+ * a new project without carrying over the git history or generated files.
23
+ *
5
24
  * @module scripts/git-clone
6
25
  */
7
26
 
27
+ const os = require('../utils/common/os');
28
+ const fs = require('fs');
29
+ const path = require('path');
30
+ const { execSync } = require('child_process');
31
+
32
+ /**
33
+ * Default list of files and directories to exclude when copying.
34
+ * These are common items that should not be copied when using a repo as a template.
35
+ */
36
+ const DEFAULT_EXCLUDES = [
37
+ '.git',
38
+ 'README.md',
39
+ 'LICENSE',
40
+ 'node_modules',
41
+ 'bower_components'
42
+ ];
43
+
44
+ /**
45
+ * Helper function to check if a command exists on the system.
46
+ *
47
+ * @param {string} cmd - The command name to check
48
+ * @returns {boolean} True if the command exists, false otherwise
49
+ */
50
+ function isCommandAvailable(cmd) {
51
+ try {
52
+ const checkCmd = process.platform === 'win32' ? `where ${cmd}` : `which ${cmd}`;
53
+ execSync(checkCmd, { stdio: 'ignore' });
54
+ return true;
55
+ } catch {
56
+ return false;
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Check if a path should be excluded from copying.
62
+ *
63
+ * @param {string} relativePath - The path relative to the source directory
64
+ * @param {string[]} excludes - Array of patterns to exclude
65
+ * @returns {boolean} True if the path should be excluded
66
+ */
67
+ function shouldExclude(relativePath, excludes) {
68
+ // Normalize path separators for cross-platform compatibility
69
+ const normalizedPath = relativePath.replace(/\\/g, '/');
70
+ const pathParts = normalizedPath.split('/');
71
+
72
+ for (const exclude of excludes) {
73
+ // Check if any part of the path matches the exclude pattern
74
+ // This handles both files and directories at any depth
75
+ if (pathParts.includes(exclude)) {
76
+ return true;
77
+ }
78
+ // Also check if the relative path starts with the exclude (for top-level matches)
79
+ if (normalizedPath === exclude || normalizedPath.startsWith(exclude + '/')) {
80
+ return true;
81
+ }
82
+ }
83
+ return false;
84
+ }
85
+
86
+ /**
87
+ * Recursively copy a directory, excluding specified patterns.
88
+ * This is a pure Node.js implementation that works on all platforms.
89
+ *
90
+ * @param {string} srcDir - Source directory path
91
+ * @param {string} destDir - Destination directory path
92
+ * @param {string[]} excludes - Array of patterns to exclude
93
+ * @param {string} [relativePath=''] - Current relative path (used for recursion)
94
+ * @param {boolean} [verbose=true] - Whether to show progress
95
+ * @returns {number} Number of files copied
96
+ */
97
+ function copyDirectoryRecursive(srcDir, destDir, excludes, relativePath = '', verbose = true) {
98
+ let filesCopied = 0;
99
+
100
+ // Read all entries in the source directory
101
+ const entries = fs.readdirSync(srcDir, { withFileTypes: true });
102
+
103
+ for (const entry of entries) {
104
+ const entryRelativePath = relativePath ? path.join(relativePath, entry.name) : entry.name;
105
+ const srcPath = path.join(srcDir, entry.name);
106
+ const destPath = path.join(destDir, entry.name);
107
+
108
+ // Check if this entry should be excluded
109
+ if (shouldExclude(entryRelativePath, excludes)) {
110
+ if (verbose) {
111
+ console.log(`Skipping: ${entryRelativePath}`);
112
+ }
113
+ continue;
114
+ }
115
+
116
+ if (entry.isDirectory()) {
117
+ // Create the directory if it doesn't exist
118
+ if (!fs.existsSync(destPath)) {
119
+ fs.mkdirSync(destPath, { recursive: true });
120
+ }
121
+ // Recursively copy contents
122
+ filesCopied += copyDirectoryRecursive(srcPath, destPath, excludes, entryRelativePath, verbose);
123
+ } else if (entry.isFile()) {
124
+ // Copy the file
125
+ fs.copyFileSync(srcPath, destPath);
126
+ if (verbose) {
127
+ console.log(`Copying: ${entryRelativePath}`);
128
+ }
129
+ filesCopied++;
130
+ } else if (entry.isSymbolicLink()) {
131
+ // Handle symbolic links - read the link target and recreate it
132
+ try {
133
+ const linkTarget = fs.readlinkSync(srcPath);
134
+ // Remove existing symlink if it exists
135
+ if (fs.existsSync(destPath)) {
136
+ fs.unlinkSync(destPath);
137
+ }
138
+ fs.symlinkSync(linkTarget, destPath);
139
+ if (verbose) {
140
+ console.log(`Linking: ${entryRelativePath} -> ${linkTarget}`);
141
+ }
142
+ filesCopied++;
143
+ } catch (err) {
144
+ console.warn(`Warning: Could not copy symbolic link ${entryRelativePath}: ${err.message}`);
145
+ }
146
+ }
147
+ }
148
+
149
+ return filesCopied;
150
+ }
151
+
8
152
  /**
9
- * Copies a repository's file structure to the current directory
10
- * excluding the .git folder, README, LICENSE, and dependency folders.
11
- * Useful for using a repo as a template.
153
+ * Pure Node.js implementation for copying repository structure.
154
+ * Uses Node.js fs module to recursively copy files while excluding
155
+ * specified patterns. This works identically on all platforms.
12
156
  *
13
157
  * @param {string[]} args - Command line arguments
14
- * @param {string} args.0 - Source repository path
158
+ * @param {string} args[0] - Source directory path
15
159
  * @returns {Promise<void>}
16
160
  */
17
- async function main(args) {
18
- // TODO: Implement structure-only clone
161
+ async function do_git_clone_nodejs(args) {
162
+ // Validate arguments
163
+ if (args.length === 0) {
164
+ console.error('Usage: git-clone <source-directory> [destination-directory]');
165
+ console.error('');
166
+ console.error('Copies files from source to destination (default: current directory),');
167
+ console.error('excluding: .git, README.md, LICENSE, node_modules, bower_components');
168
+ console.error('');
169
+ console.error('Examples:');
170
+ console.error(' git-clone /path/to/template-repo');
171
+ console.error(' git-clone ../my-template ./new-project');
172
+ process.exit(1);
173
+ }
174
+
175
+ // Parse source path - remove trailing slash for consistency
176
+ const sourcePath = path.resolve(args[0].replace(/\/+$/, ''));
177
+
178
+ // Parse destination path - default to current directory
179
+ const destPath = args[1] ? path.resolve(args[1]) : process.cwd();
180
+
181
+ // Validate source directory exists
182
+ if (!fs.existsSync(sourcePath)) {
183
+ console.error(`Error: Source directory does not exist: ${sourcePath}`);
184
+ process.exit(1);
185
+ }
186
+
187
+ // Validate source is a directory
188
+ const sourceStats = fs.statSync(sourcePath);
189
+ if (!sourceStats.isDirectory()) {
190
+ console.error(`Error: Source path is not a directory: ${sourcePath}`);
191
+ process.exit(1);
192
+ }
193
+
194
+ // Create destination directory if it doesn't exist
195
+ if (!fs.existsSync(destPath)) {
196
+ console.log(`Creating destination directory: ${destPath}`);
197
+ fs.mkdirSync(destPath, { recursive: true });
198
+ }
199
+
200
+ // Validate destination is a directory
201
+ const destStats = fs.statSync(destPath);
202
+ if (!destStats.isDirectory()) {
203
+ console.error(`Error: Destination path is not a directory: ${destPath}`);
204
+ process.exit(1);
205
+ }
206
+
207
+ // Prevent copying a directory into itself
208
+ const resolvedSource = fs.realpathSync(sourcePath);
209
+ const resolvedDest = fs.realpathSync(destPath);
210
+ if (resolvedDest.startsWith(resolvedSource + path.sep) || resolvedSource === resolvedDest) {
211
+ console.error('Error: Cannot copy a directory into itself or a subdirectory of itself.');
212
+ process.exit(1);
213
+ }
214
+
215
+ console.log(`Copying from: ${sourcePath}`);
216
+ console.log(`Copying to: ${destPath}`);
217
+ console.log(`Excluding: ${DEFAULT_EXCLUDES.join(', ')}`);
218
+ console.log('');
219
+
220
+ // Perform the copy
221
+ const filesCopied = copyDirectoryRecursive(sourcePath, destPath, DEFAULT_EXCLUDES, '', true);
222
+
223
+ console.log('');
224
+ console.log(`Done! Copied ${filesCopied} file(s).`);
225
+ }
226
+
227
+ /**
228
+ * Copy repository structure on macOS.
229
+ *
230
+ * Uses rsync if available (the original implementation approach) for better
231
+ * performance with large directories. Falls back to pure Node.js if rsync
232
+ * is not installed.
233
+ *
234
+ * rsync advantages:
235
+ * - Shows progress for large files
236
+ * - Preserves permissions and timestamps more reliably
237
+ * - Handles edge cases with special files
238
+ *
239
+ * @param {string[]} args - Command line arguments
240
+ * @returns {Promise<void>}
241
+ */
242
+ async function do_git_clone_macos(args) {
243
+ // rsync is installed by default on macOS, so use it for better performance
244
+ if (isCommandAvailable('rsync')) {
245
+ return do_git_clone_rsync(args);
246
+ }
247
+ // Fallback to pure Node.js implementation
248
+ return do_git_clone_nodejs(args);
249
+ }
250
+
251
+ /**
252
+ * Copy repository structure on Ubuntu.
253
+ *
254
+ * Uses rsync if available for better performance. rsync is commonly
255
+ * installed on Ubuntu systems. Falls back to pure Node.js if not available.
256
+ *
257
+ * @param {string[]} args - Command line arguments
258
+ * @returns {Promise<void>}
259
+ */
260
+ async function do_git_clone_ubuntu(args) {
261
+ if (isCommandAvailable('rsync')) {
262
+ return do_git_clone_rsync(args);
263
+ }
264
+ // Fallback to pure Node.js implementation
265
+ return do_git_clone_nodejs(args);
266
+ }
267
+
268
+ /**
269
+ * Copy repository structure on Raspberry Pi OS.
270
+ *
271
+ * Uses rsync if available for better performance. Falls back to
272
+ * pure Node.js implementation if rsync is not installed.
273
+ *
274
+ * @param {string[]} args - Command line arguments
275
+ * @returns {Promise<void>}
276
+ */
277
+ async function do_git_clone_raspbian(args) {
278
+ if (isCommandAvailable('rsync')) {
279
+ return do_git_clone_rsync(args);
280
+ }
281
+ return do_git_clone_nodejs(args);
282
+ }
283
+
284
+ /**
285
+ * Copy repository structure on Amazon Linux.
286
+ *
287
+ * Uses rsync if available for better performance. Falls back to
288
+ * pure Node.js implementation if rsync is not installed.
289
+ *
290
+ * @param {string[]} args - Command line arguments
291
+ * @returns {Promise<void>}
292
+ */
293
+ async function do_git_clone_amazon_linux(args) {
294
+ if (isCommandAvailable('rsync')) {
295
+ return do_git_clone_rsync(args);
296
+ }
297
+ return do_git_clone_nodejs(args);
298
+ }
299
+
300
+ /**
301
+ * Copy repository structure on Windows Command Prompt.
302
+ *
303
+ * Windows does not have rsync by default, so this uses the pure Node.js
304
+ * implementation which works reliably on Windows.
305
+ *
306
+ * @param {string[]} args - Command line arguments
307
+ * @returns {Promise<void>}
308
+ */
309
+ async function do_git_clone_cmd(args) {
310
+ // Windows doesn't have rsync by default, use pure Node.js
311
+ return do_git_clone_nodejs(args);
312
+ }
313
+
314
+ /**
315
+ * Copy repository structure on Windows PowerShell.
316
+ *
317
+ * Uses the pure Node.js implementation for reliable cross-platform behavior.
318
+ * PowerShell's Copy-Item doesn't have the same exclude capabilities as rsync.
319
+ *
320
+ * @param {string[]} args - Command line arguments
321
+ * @returns {Promise<void>}
322
+ */
323
+ async function do_git_clone_powershell(args) {
324
+ // Use pure Node.js for consistent behavior
325
+ return do_git_clone_nodejs(args);
326
+ }
327
+
328
+ /**
329
+ * Copy repository structure from Git Bash on Windows.
330
+ *
331
+ * Git Bash may have rsync available if installed separately (e.g., via MSYS2).
332
+ * Falls back to pure Node.js if rsync is not available.
333
+ *
334
+ * @param {string[]} args - Command line arguments
335
+ * @returns {Promise<void>}
336
+ */
337
+ async function do_git_clone_gitbash(args) {
338
+ // Git Bash might have rsync if user installed it
339
+ if (isCommandAvailable('rsync')) {
340
+ return do_git_clone_rsync(args);
341
+ }
342
+ return do_git_clone_nodejs(args);
343
+ }
344
+
345
+ /**
346
+ * Implementation using rsync for Unix-like systems.
347
+ *
348
+ * This matches the original bash function behavior exactly:
349
+ * rsync -av --progress $* ./ --exclude .git --exclude README.md ...
350
+ *
351
+ * rsync provides:
352
+ * - Progress indication for large transfers
353
+ * - Efficient incremental updates
354
+ * - Proper handling of permissions and special files
355
+ *
356
+ * @param {string[]} args - Command line arguments
357
+ * @returns {Promise<void>}
358
+ */
359
+ async function do_git_clone_rsync(args) {
360
+ // Validate arguments
361
+ if (args.length === 0) {
362
+ console.error('Usage: git-clone <source-directory> [destination-directory]');
363
+ console.error('');
364
+ console.error('Copies files from source to destination (default: current directory),');
365
+ console.error('excluding: .git, README.md, LICENSE, node_modules, bower_components');
366
+ console.error('');
367
+ console.error('Examples:');
368
+ console.error(' git-clone /path/to/template-repo');
369
+ console.error(' git-clone ../my-template ./new-project');
370
+ process.exit(1);
371
+ }
372
+
373
+ // Parse source path - ensure it ends with / for rsync directory behavior
374
+ let sourcePath = path.resolve(args[0]);
375
+ if (!sourcePath.endsWith('/')) {
376
+ sourcePath += '/';
377
+ }
378
+
379
+ // Parse destination path - default to current directory
380
+ let destPath = args[1] ? path.resolve(args[1]) : process.cwd();
381
+ if (!destPath.endsWith('/')) {
382
+ destPath += '/';
383
+ }
384
+
385
+ // Validate source directory exists
386
+ const sourceDir = sourcePath.replace(/\/+$/, '');
387
+ if (!fs.existsSync(sourceDir)) {
388
+ console.error(`Error: Source directory does not exist: ${sourceDir}`);
389
+ process.exit(1);
390
+ }
391
+
392
+ // Validate source is a directory
393
+ const sourceStats = fs.statSync(sourceDir);
394
+ if (!sourceStats.isDirectory()) {
395
+ console.error(`Error: Source path is not a directory: ${sourceDir}`);
396
+ process.exit(1);
397
+ }
398
+
399
+ // Create destination directory if it doesn't exist
400
+ const destDir = destPath.replace(/\/+$/, '');
401
+ if (!fs.existsSync(destDir)) {
402
+ console.log(`Creating destination directory: ${destDir}`);
403
+ fs.mkdirSync(destDir, { recursive: true });
404
+ }
405
+
406
+ // Build the rsync command matching the original behavior
407
+ // Original: rsync -av --progress $* ./ --exclude .git --exclude README.md ...
408
+ const excludeArgs = DEFAULT_EXCLUDES.map(exc => `--exclude "${exc}"`).join(' ');
409
+ const rsyncCmd = `rsync -av --progress "${sourcePath}" "${destPath}" ${excludeArgs}`;
410
+
411
+ console.log(`Executing: ${rsyncCmd}`);
412
+ console.log('');
413
+
414
+ try {
415
+ execSync(rsyncCmd, { stdio: 'inherit' });
416
+ console.log('');
417
+ console.log('Done!');
418
+ } catch (error) {
419
+ console.error('Error: rsync command failed.');
420
+ console.error('Falling back to Node.js implementation...');
421
+ console.log('');
422
+ // Fall back to Node.js implementation
423
+ return do_git_clone_nodejs(args);
424
+ }
425
+ }
426
+
427
+ /**
428
+ * Main entry point - detects environment and executes appropriate implementation.
429
+ *
430
+ * The "git-clone" command copies files from a source directory (typically a
431
+ * git repository) to the current directory or a specified destination,
432
+ * excluding common files that should not be copied when using a repo as
433
+ * a template.
434
+ *
435
+ * This is useful when you want to:
436
+ * - Use an existing repository as a starting point for a new project
437
+ * - Copy project structure without git history
438
+ * - Skip dependency folders that should be reinstalled fresh
439
+ *
440
+ * @param {string[]} args - Command line arguments
441
+ * @returns {Promise<void>}
442
+ */
443
+ async function do_git_clone(args) {
444
+ const platform = os.detect();
445
+
446
+ const handlers = {
447
+ 'macos': do_git_clone_macos,
448
+ 'ubuntu': do_git_clone_ubuntu,
449
+ 'debian': do_git_clone_ubuntu,
450
+ 'raspbian': do_git_clone_raspbian,
451
+ 'amazon_linux': do_git_clone_amazon_linux,
452
+ 'rhel': do_git_clone_amazon_linux,
453
+ 'fedora': do_git_clone_ubuntu,
454
+ 'linux': do_git_clone_ubuntu,
455
+ 'wsl': do_git_clone_ubuntu,
456
+ 'cmd': do_git_clone_cmd,
457
+ 'windows': do_git_clone_cmd,
458
+ 'powershell': do_git_clone_powershell,
459
+ 'gitbash': do_git_clone_gitbash
460
+ };
461
+
462
+ const handler = handlers[platform.type];
463
+ if (!handler) {
464
+ console.error(`Platform '${platform.type}' is not supported for this command.`);
465
+ console.error('');
466
+ console.error('Supported platforms:');
467
+ console.error(' - macOS');
468
+ console.error(' - Ubuntu, Debian, and other Linux distributions');
469
+ console.error(' - Raspberry Pi OS');
470
+ console.error(' - Amazon Linux, RHEL, Fedora');
471
+ console.error(' - Windows (CMD, PowerShell, Git Bash)');
472
+ process.exit(1);
473
+ }
474
+
475
+ await handler(args);
19
476
  }
20
477
 
21
- module.exports = { main };
478
+ module.exports = {
479
+ main: do_git_clone,
480
+ do_git_clone,
481
+ do_git_clone_nodejs,
482
+ do_git_clone_macos,
483
+ do_git_clone_ubuntu,
484
+ do_git_clone_raspbian,
485
+ do_git_clone_amazon_linux,
486
+ do_git_clone_cmd,
487
+ do_git_clone_powershell,
488
+ do_git_clone_gitbash
489
+ };
22
490
 
23
491
  if (require.main === module) {
24
- main(process.argv.slice(2));
492
+ do_git_clone(process.argv.slice(2));
25
493
  }