@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,24 +1,309 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * @fileoverview Execute a command for each line of input.
4
+ * map - Execute a command for each line of input
5
+ *
6
+ * Migrated from legacy dotfiles alias.
7
+ * Original: alias map="xargs -n1"
8
+ *
9
+ * This script reads lines from stdin and executes the specified command
10
+ * for each line, similar to `xargs -n1`. It allows you to "map" a command
11
+ * over a list of inputs.
12
+ *
13
+ * Usage examples:
14
+ * echo -e "file1.txt\nfile2.txt" | map cat
15
+ * ls *.js | map wc -l
16
+ * git branch | map git log -1 --oneline
17
+ *
18
+ * The input line is passed as the last argument to the command. For example:
19
+ * echo "hello" | map echo "Processing:"
20
+ * # Executes: echo "Processing:" "hello"
21
+ *
5
22
  * @module scripts/map
6
23
  */
7
24
 
25
+ const os = require('../utils/common/os');
26
+ const { spawn } = require('child_process');
27
+ const readline = require('readline');
28
+
29
+ /**
30
+ * Pure Node.js implementation that works on any platform.
31
+ *
32
+ * This function reads lines from stdin and executes the specified command
33
+ * for each line. The input line is appended as the last argument to the command.
34
+ *
35
+ * Why this works cross-platform:
36
+ * - readline module handles stdin consistently across all platforms
37
+ * - spawn works identically on macOS, Linux, and Windows
38
+ * - No platform-specific shell features are used
39
+ *
40
+ * @param {string[]} args - Command and its arguments. The first element is the
41
+ * command to run, remaining elements are arguments.
42
+ * The input line is appended as the final argument.
43
+ * @returns {Promise<void>}
44
+ */
45
+ async function do_map_nodejs(args) {
46
+ // If no command provided, show usage information
47
+ if (args.length === 0) {
48
+ console.log('Usage: map <command> [arguments...]');
49
+ console.log('');
50
+ console.log('Reads lines from stdin and executes the command for each line.');
51
+ console.log('The input line is passed as the last argument to the command.');
52
+ console.log('');
53
+ console.log('Examples:');
54
+ console.log(' echo -e "file1.txt\\nfile2.txt" | map cat');
55
+ console.log(' ls *.js | map wc -l');
56
+ console.log(' find . -name "*.txt" | map head -1');
57
+ console.log('');
58
+ console.log('Similar to: xargs -n1');
59
+ return;
60
+ }
61
+
62
+ // Extract the command (first argument) and its base arguments (remaining arguments)
63
+ const command = args[0];
64
+ const baseArgs = args.slice(1);
65
+
66
+ // Create readline interface to read lines from stdin
67
+ const rl = readline.createInterface({
68
+ input: process.stdin,
69
+ // Don't output to terminal - we're reading piped data
70
+ terminal: false
71
+ });
72
+
73
+ // Collect all lines first, then process them sequentially
74
+ // This ensures commands complete in order and errors are handled properly
75
+ const lines = [];
76
+
77
+ for await (const line of rl) {
78
+ // Skip empty lines to avoid passing empty arguments
79
+ const trimmedLine = line.trim();
80
+ if (trimmedLine !== '') {
81
+ lines.push(trimmedLine);
82
+ }
83
+ }
84
+
85
+ // If no input was provided (empty stdin), inform the user
86
+ if (lines.length === 0) {
87
+ console.error('No input received. Pipe data to this command:');
88
+ console.error(' echo "item1\\nitem2" | map <command>');
89
+ return;
90
+ }
91
+
92
+ // Process each line sequentially
93
+ // Sequential processing ensures output is predictable and ordered
94
+ for (const inputLine of lines) {
95
+ // Build the full argument list: base args + the input line
96
+ const fullArgs = [...baseArgs, inputLine];
97
+
98
+ try {
99
+ // Execute the command and wait for it to complete
100
+ await executeCommand(command, fullArgs);
101
+ } catch (error) {
102
+ // Continue processing remaining lines even if one fails
103
+ // This matches xargs behavior by default
104
+ console.error(`Error processing "${inputLine}": ${error.message}`);
105
+ }
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Execute a command with given arguments and return a promise.
111
+ *
112
+ * Uses spawn instead of exec to:
113
+ * - Handle large output without buffer limits
114
+ * - Stream output in real-time
115
+ * - Better handle signals and process lifecycle
116
+ *
117
+ * @param {string} command - The command to execute
118
+ * @param {string[]} args - Arguments to pass to the command
119
+ * @returns {Promise<void>} Resolves when command completes, rejects on error
120
+ */
121
+ function executeCommand(command, args) {
122
+ return new Promise((resolve, reject) => {
123
+ // Determine if we're on Windows to set appropriate shell option
124
+ const isWindows = process.platform === 'win32';
125
+
126
+ // Spawn the child process
127
+ // - shell: true allows running shell built-ins and handles PATH lookup
128
+ // - stdio: 'inherit' connects child's stdin/stdout/stderr to parent
129
+ const child = spawn(command, args, {
130
+ shell: true,
131
+ stdio: 'inherit',
132
+ // On Windows, use cmd.exe; on Unix, use default shell
133
+ ...(isWindows && { windowsHide: true })
134
+ });
135
+
136
+ // Handle process completion
137
+ child.on('close', (code) => {
138
+ if (code === 0) {
139
+ resolve();
140
+ } else {
141
+ // Non-zero exit code indicates command failed
142
+ reject(new Error(`Command exited with code ${code}`));
143
+ }
144
+ });
145
+
146
+ // Handle spawn errors (command not found, permission denied, etc.)
147
+ child.on('error', (error) => {
148
+ reject(error);
149
+ });
150
+ });
151
+ }
152
+
153
+ /**
154
+ * Execute a command for each line of input on macOS.
155
+ *
156
+ * macOS can use the pure Node.js implementation since no platform-specific
157
+ * features are required for this functionality.
158
+ *
159
+ * @param {string[]} args - Command line arguments
160
+ * @returns {Promise<void>}
161
+ */
162
+ async function do_map_macos(args) {
163
+ return do_map_nodejs(args);
164
+ }
165
+
166
+ /**
167
+ * Execute a command for each line of input on Ubuntu.
168
+ *
169
+ * Ubuntu can use the pure Node.js implementation since no platform-specific
170
+ * features are required for this functionality.
171
+ *
172
+ * @param {string[]} args - Command line arguments
173
+ * @returns {Promise<void>}
174
+ */
175
+ async function do_map_ubuntu(args) {
176
+ return do_map_nodejs(args);
177
+ }
178
+
179
+ /**
180
+ * Execute a command for each line of input on Raspberry Pi OS.
181
+ *
182
+ * Raspberry Pi OS can use the pure Node.js implementation since no
183
+ * platform-specific features are required for this functionality.
184
+ *
185
+ * @param {string[]} args - Command line arguments
186
+ * @returns {Promise<void>}
187
+ */
188
+ async function do_map_raspbian(args) {
189
+ return do_map_nodejs(args);
190
+ }
191
+
8
192
  /**
9
- * Reads lines from stdin and executes the specified command
10
- * for each line, similar to `xargs -n1`.
193
+ * Execute a command for each line of input on Amazon Linux.
194
+ *
195
+ * Amazon Linux can use the pure Node.js implementation since no
196
+ * platform-specific features are required for this functionality.
11
197
  *
12
198
  * @param {string[]} args - Command line arguments
13
- * @param {string} args.0 - Command to execute for each input line
14
199
  * @returns {Promise<void>}
15
200
  */
16
- async function main(args) {
17
- // TODO: Implement xargs-like mapping
201
+ async function do_map_amazon_linux(args) {
202
+ return do_map_nodejs(args);
203
+ }
204
+
205
+ /**
206
+ * Execute a command for each line of input in Windows Command Prompt.
207
+ *
208
+ * Windows CMD can use the pure Node.js implementation. The spawn command
209
+ * with shell: true automatically uses cmd.exe on Windows.
210
+ *
211
+ * @param {string[]} args - Command line arguments
212
+ * @returns {Promise<void>}
213
+ */
214
+ async function do_map_cmd(args) {
215
+ return do_map_nodejs(args);
216
+ }
217
+
218
+ /**
219
+ * Execute a command for each line of input in Windows PowerShell.
220
+ *
221
+ * Windows PowerShell can use the pure Node.js implementation. The spawn
222
+ * command with shell: true works correctly from PowerShell.
223
+ *
224
+ * @param {string[]} args - Command line arguments
225
+ * @returns {Promise<void>}
226
+ */
227
+ async function do_map_powershell(args) {
228
+ return do_map_nodejs(args);
229
+ }
230
+
231
+ /**
232
+ * Execute a command for each line of input in Git Bash.
233
+ *
234
+ * Git Bash can use the pure Node.js implementation since no platform-specific
235
+ * features are required for this functionality.
236
+ *
237
+ * @param {string[]} args - Command line arguments
238
+ * @returns {Promise<void>}
239
+ */
240
+ async function do_map_gitbash(args) {
241
+ return do_map_nodejs(args);
242
+ }
243
+
244
+ /**
245
+ * Main entry point - detects environment and executes appropriate implementation.
246
+ *
247
+ * The "map" command reads lines from stdin and executes a command for each line,
248
+ * similar to `xargs -n1`. This is useful for applying a command to each item in
249
+ * a list, such as:
250
+ *
251
+ * - Processing each file from a directory listing
252
+ * - Applying a git command to multiple branches
253
+ * - Running a script on each item from a search result
254
+ *
255
+ * @param {string[]} args - Command line arguments. First argument is the command
256
+ * to run, remaining arguments are passed to that command.
257
+ * @returns {Promise<void>}
258
+ */
259
+ async function do_map(args) {
260
+ const platform = os.detect();
261
+
262
+ const handlers = {
263
+ 'macos': do_map_macos,
264
+ 'ubuntu': do_map_ubuntu,
265
+ 'debian': do_map_ubuntu,
266
+ 'raspbian': do_map_raspbian,
267
+ 'amazon_linux': do_map_amazon_linux,
268
+ 'rhel': do_map_amazon_linux,
269
+ 'fedora': do_map_ubuntu,
270
+ 'linux': do_map_ubuntu,
271
+ 'wsl': do_map_ubuntu,
272
+ 'cmd': do_map_cmd,
273
+ 'windows': do_map_cmd,
274
+ 'powershell': do_map_powershell,
275
+ 'gitbash': do_map_gitbash
276
+ };
277
+
278
+ const handler = handlers[platform.type];
279
+ if (!handler) {
280
+ console.error(`Platform '${platform.type}' is not supported for this command.`);
281
+ console.error('');
282
+ console.error('Supported platforms:');
283
+ console.error(' - macOS');
284
+ console.error(' - Ubuntu, Debian, and other Linux distributions');
285
+ console.error(' - Raspberry Pi OS');
286
+ console.error(' - Amazon Linux, RHEL, Fedora');
287
+ console.error(' - Windows (CMD, PowerShell, Git Bash)');
288
+ process.exit(1);
289
+ }
290
+
291
+ await handler(args);
18
292
  }
19
293
 
20
- module.exports = { main };
294
+ module.exports = {
295
+ main: do_map,
296
+ do_map,
297
+ do_map_nodejs,
298
+ do_map_macos,
299
+ do_map_ubuntu,
300
+ do_map_raspbian,
301
+ do_map_amazon_linux,
302
+ do_map_cmd,
303
+ do_map_powershell,
304
+ do_map_gitbash
305
+ };
21
306
 
22
307
  if (require.main === module) {
23
- main(process.argv.slice(2));
308
+ do_map(process.argv.slice(2));
24
309
  }
@@ -1,24 +1,304 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * @fileoverview Create directory and navigate into it.
4
+ * mkd - Create a new directory and display navigation instructions
5
+ *
6
+ * Migrated from legacy dotfiles function.
7
+ * Original:
8
+ * mkd() {
9
+ * mkdir -p "$@" && cd "$@"
10
+ * }
11
+ *
12
+ * This script creates a new directory (including all necessary parent directories)
13
+ * and outputs instructions for navigating into it. The original shell function could
14
+ * change the working directory directly, but since Node.js scripts run in a subprocess,
15
+ * they cannot modify the parent shell's working directory.
16
+ *
17
+ * Usage:
18
+ * mkd my-project # Creates ./my-project
19
+ * mkd path/to/nested/directory # Creates all parent directories as needed
20
+ * mkd ~/projects/new-app # Works with home directory paths
21
+ *
5
22
  * @module scripts/mkd
6
23
  */
7
24
 
25
+ const os = require('../utils/common/os');
26
+ const fs = require('fs');
27
+ const path = require('path');
28
+
29
+ /**
30
+ * Expands a path that starts with ~ to the user's home directory.
31
+ *
32
+ * Node.js doesn't automatically expand tilde in paths like shells do,
33
+ * so we need to handle this manually for user convenience.
34
+ *
35
+ * @param {string} inputPath - The path that may contain a leading ~
36
+ * @returns {string} The expanded path with ~ replaced by home directory
37
+ */
38
+ function expandTilde(inputPath) {
39
+ // If the path starts with ~, replace it with the home directory
40
+ if (inputPath.startsWith('~')) {
41
+ const homeDir = os.getHomeDir();
42
+ // Handle both ~/path and ~ alone
43
+ return inputPath.replace(/^~/, homeDir);
44
+ }
45
+ return inputPath;
46
+ }
47
+
48
+ /**
49
+ * Pure Node.js implementation for creating directories.
50
+ *
51
+ * Uses the Node.js fs module to create directories recursively,
52
+ * equivalent to the shell's `mkdir -p` command. This approach is:
53
+ * - Cross-platform (works identically on all OSes)
54
+ * - Native to Node.js (no shell commands needed)
55
+ * - Idempotent (safe to run multiple times)
56
+ *
57
+ * @param {string[]} args - Command line arguments
58
+ * @param {string} args.0 - Directory path to create
59
+ * @returns {Promise<void>}
60
+ */
61
+ async function do_mkd_nodejs(args) {
62
+ // Validate arguments
63
+ if (args.length === 0) {
64
+ console.error('Usage: mkd <directory>');
65
+ console.error('');
66
+ console.error('Creates a new directory, including parent directories if needed.');
67
+ console.error('');
68
+ console.error('Examples:');
69
+ console.error(' mkd my-project');
70
+ console.error(' mkd path/to/nested/directory');
71
+ console.error(' mkd ~/projects/new-app');
72
+ process.exit(1);
73
+ }
74
+
75
+ // Get the directory path from arguments
76
+ // The original bash function used "$@" which takes all arguments,
77
+ // but typically mkd is used with a single directory path
78
+ const rawPath = args.join(' ');
79
+
80
+ // Expand tilde if present (shells do this automatically, Node.js doesn't)
81
+ const expandedPath = expandTilde(rawPath);
82
+
83
+ // Resolve to absolute path for clearer output
84
+ const absolutePath = path.resolve(expandedPath);
85
+
86
+ // Check if the path already exists
87
+ if (fs.existsSync(absolutePath)) {
88
+ // Check if it's a directory or a file
89
+ const stats = fs.statSync(absolutePath);
90
+ if (stats.isDirectory()) {
91
+ // Directory already exists - this is fine (idempotent behavior)
92
+ console.log(`Directory already exists: ${absolutePath}`);
93
+ console.log('');
94
+ console.log(`To navigate there, run: cd "${absolutePath}"`);
95
+ return;
96
+ } else {
97
+ // A file with the same name exists - this is an error
98
+ console.error(`Error: A file with this name already exists: ${absolutePath}`);
99
+ console.error('Cannot create a directory with the same name as an existing file.');
100
+ process.exit(1);
101
+ }
102
+ }
103
+
104
+ // Create the directory (and all parent directories)
105
+ // fs.mkdirSync with recursive:true is equivalent to mkdir -p
106
+ try {
107
+ fs.mkdirSync(absolutePath, { recursive: true });
108
+ } catch (error) {
109
+ // Handle permission errors and other filesystem errors
110
+ if (error.code === 'EACCES') {
111
+ console.error(`Error: Permission denied. Cannot create directory: ${absolutePath}`);
112
+ console.error('You may need to run this command with elevated privileges.');
113
+ } else if (error.code === 'ENOENT') {
114
+ console.error(`Error: Invalid path: ${absolutePath}`);
115
+ } else if (error.code === 'ENOTDIR') {
116
+ console.error(`Error: A component of the path is not a directory: ${absolutePath}`);
117
+ } else {
118
+ console.error(`Error: Failed to create directory: ${error.message}`);
119
+ }
120
+ process.exit(1);
121
+ }
122
+
123
+ // Verify the directory was created
124
+ if (!fs.existsSync(absolutePath)) {
125
+ console.error(`Error: Directory creation appeared to succeed but directory does not exist.`);
126
+ process.exit(1);
127
+ }
128
+
129
+ // Success! Print the result
130
+ // Note: Unlike the original shell function, we cannot change the parent shell's
131
+ // working directory. We provide instructions instead.
132
+ console.log(`Created directory: ${absolutePath}`);
133
+ console.log('');
134
+ console.log(`To navigate there, run: cd "${absolutePath}"`);
135
+ }
136
+
137
+ /**
138
+ * Create directory on macOS.
139
+ *
140
+ * Uses the pure Node.js implementation since fs.mkdirSync works
141
+ * identically on macOS as on other platforms.
142
+ *
143
+ * @param {string[]} args - Command line arguments
144
+ * @returns {Promise<void>}
145
+ */
146
+ async function do_mkd_macos(args) {
147
+ return do_mkd_nodejs(args);
148
+ }
149
+
150
+ /**
151
+ * Create directory on Ubuntu.
152
+ *
153
+ * Uses the pure Node.js implementation since fs.mkdirSync works
154
+ * identically on Linux as on other platforms.
155
+ *
156
+ * @param {string[]} args - Command line arguments
157
+ * @returns {Promise<void>}
158
+ */
159
+ async function do_mkd_ubuntu(args) {
160
+ return do_mkd_nodejs(args);
161
+ }
162
+
163
+ /**
164
+ * Create directory on Raspberry Pi OS.
165
+ *
166
+ * Uses the pure Node.js implementation since fs.mkdirSync works
167
+ * identically on Raspberry Pi OS as on other platforms.
168
+ *
169
+ * @param {string[]} args - Command line arguments
170
+ * @returns {Promise<void>}
171
+ */
172
+ async function do_mkd_raspbian(args) {
173
+ return do_mkd_nodejs(args);
174
+ }
175
+
8
176
  /**
9
- * Creates a new directory (including parent directories) and
10
- * outputs the path for shell navigation (e.g., cd $(mkd mydir)).
177
+ * Create directory on Amazon Linux.
178
+ *
179
+ * Uses the pure Node.js implementation since fs.mkdirSync works
180
+ * identically on Amazon Linux as on other platforms.
181
+ *
182
+ * @param {string[]} args - Command line arguments
183
+ * @returns {Promise<void>}
184
+ */
185
+ async function do_mkd_amazon_linux(args) {
186
+ return do_mkd_nodejs(args);
187
+ }
188
+
189
+ /**
190
+ * Create directory in Windows Command Prompt.
191
+ *
192
+ * Uses the pure Node.js implementation since fs.mkdirSync works
193
+ * identically on Windows as on other platforms. The Node.js fs module
194
+ * handles Windows path separators automatically.
195
+ *
196
+ * @param {string[]} args - Command line arguments
197
+ * @returns {Promise<void>}
198
+ */
199
+ async function do_mkd_cmd(args) {
200
+ return do_mkd_nodejs(args);
201
+ }
202
+
203
+ /**
204
+ * Create directory in Windows PowerShell.
205
+ *
206
+ * Uses the pure Node.js implementation since fs.mkdirSync works
207
+ * identically across all platforms and shells.
208
+ *
209
+ * @param {string[]} args - Command line arguments
210
+ * @returns {Promise<void>}
211
+ */
212
+ async function do_mkd_powershell(args) {
213
+ return do_mkd_nodejs(args);
214
+ }
215
+
216
+ /**
217
+ * Create directory in Git Bash on Windows.
218
+ *
219
+ * Uses the pure Node.js implementation. Git Bash understands both
220
+ * Unix-style and Windows-style paths, and Node.js handles the conversion.
221
+ *
222
+ * @param {string[]} args - Command line arguments
223
+ * @returns {Promise<void>}
224
+ */
225
+ async function do_mkd_gitbash(args) {
226
+ return do_mkd_nodejs(args);
227
+ }
228
+
229
+ /**
230
+ * Main entry point - detects environment and executes appropriate implementation.
231
+ *
232
+ * The "mkd" (make directory) command is a developer convenience tool that:
233
+ * 1. Creates a new directory, including all parent directories if needed
234
+ * 2. Provides the cd command to navigate to the new directory
235
+ *
236
+ * This replicates the shell function pattern of creating a directory and
237
+ * immediately entering it, common in developer workflows when starting
238
+ * new projects or organizing files.
239
+ *
240
+ * Note: Unlike the original shell function, this script cannot change the
241
+ * parent shell's working directory. Node.js scripts run in a subprocess and
242
+ * cannot affect the parent process's environment. The script outputs the
243
+ * cd command for the user to run.
244
+ *
245
+ * For a true "mkdir and cd" experience, users can use a shell alias:
246
+ * alias mkd='function _mkd(){ mkd "$1" && cd "$1"; }; _mkd'
247
+ * Or use command substitution:
248
+ * cd $(mkd --quiet my-dir) # If --quiet flag were implemented
11
249
  *
12
250
  * @param {string[]} args - Command line arguments
13
251
  * @param {string} args.0 - Directory path to create
14
252
  * @returns {Promise<void>}
15
253
  */
16
- async function main(args) {
17
- // TODO: Implement mkdir with cd
254
+ async function do_mkd(args) {
255
+ const platform = os.detect();
256
+
257
+ const handlers = {
258
+ 'macos': do_mkd_macos,
259
+ 'ubuntu': do_mkd_ubuntu,
260
+ 'debian': do_mkd_ubuntu,
261
+ 'raspbian': do_mkd_raspbian,
262
+ 'amazon_linux': do_mkd_amazon_linux,
263
+ 'rhel': do_mkd_amazon_linux,
264
+ 'fedora': do_mkd_ubuntu,
265
+ 'linux': do_mkd_ubuntu,
266
+ 'wsl': do_mkd_ubuntu,
267
+ 'cmd': do_mkd_cmd,
268
+ 'windows': do_mkd_cmd,
269
+ 'powershell': do_mkd_powershell,
270
+ 'gitbash': do_mkd_gitbash
271
+ };
272
+
273
+ const handler = handlers[platform.type];
274
+ if (!handler) {
275
+ console.error(`Platform '${platform.type}' is not supported for this command.`);
276
+ console.error('');
277
+ console.error('Supported platforms:');
278
+ console.error(' - macOS');
279
+ console.error(' - Ubuntu, Debian, and other Linux distributions');
280
+ console.error(' - Raspberry Pi OS');
281
+ console.error(' - Amazon Linux, RHEL, Fedora');
282
+ console.error(' - Windows (CMD, PowerShell, Git Bash)');
283
+ process.exit(1);
284
+ }
285
+
286
+ await handler(args);
18
287
  }
19
288
 
20
- module.exports = { main };
289
+ module.exports = {
290
+ main: do_mkd,
291
+ do_mkd,
292
+ do_mkd_nodejs,
293
+ do_mkd_macos,
294
+ do_mkd_ubuntu,
295
+ do_mkd_raspbian,
296
+ do_mkd_amazon_linux,
297
+ do_mkd_cmd,
298
+ do_mkd_powershell,
299
+ do_mkd_gitbash
300
+ };
21
301
 
22
302
  if (require.main === module) {
23
- main(process.argv.slice(2));
303
+ do_mkd(process.argv.slice(2));
24
304
  }