@fredlackey/devutils 0.0.1 → 0.0.2

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 (257) 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
package/src/scripts/o.js CHANGED
@@ -1,24 +1,511 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * @fileoverview Open file or folder in system file manager.
4
+ * o - Open file or folder in system's default application
5
+ *
6
+ * Migrated from legacy dotfiles alias.
7
+ * Original aliases:
8
+ * macOS: alias o="open"
9
+ * Ubuntu: alias o="xdg-open"
10
+ *
11
+ * This script opens a file or folder using the operating system's default
12
+ * application. For directories, this typically opens the file manager
13
+ * (Finder on macOS, Nautilus/Files on Ubuntu, Explorer on Windows).
14
+ * For files, it opens the associated application based on file type.
15
+ *
16
+ * If no path is specified, opens the current directory in the file manager.
17
+ *
5
18
  * @module scripts/o
6
19
  */
7
20
 
21
+ const os = require('../utils/common/os');
22
+ const fs = require('fs');
23
+ const path = require('path');
24
+ const { execSync, exec } = require('child_process');
25
+
26
+ /**
27
+ * Helper function to check if a command exists on the system.
28
+ * Used to detect which open command is available on Linux.
29
+ *
30
+ * @param {string} cmd - The command name to check
31
+ * @returns {boolean} True if the command exists, false otherwise
32
+ */
33
+ function isCommandAvailable(cmd) {
34
+ try {
35
+ // Use 'which' on Unix-like systems, 'where' on Windows
36
+ const checkCmd = process.platform === 'win32' ? `where ${cmd}` : `which ${cmd}`;
37
+ execSync(checkCmd, { stdio: 'ignore' });
38
+ return true;
39
+ } catch {
40
+ return false;
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Pure Node.js implementation - NOT APPLICABLE for this script.
46
+ *
47
+ * Opening files/folders in the system's default application requires
48
+ * OS-level integration that cannot be done in pure Node.js. Each platform
49
+ * has its own mechanism:
50
+ * - macOS: 'open' command (part of macOS)
51
+ * - Linux: 'xdg-open' or desktop-specific commands
52
+ * - Windows: 'start' command or 'explorer.exe'
53
+ *
54
+ * @param {string[]} args - Command line arguments
55
+ * @returns {Promise<void>}
56
+ * @throws {Error} Always throws - this function should not be called directly
57
+ */
58
+ async function do_o_nodejs(args) {
59
+ // Opening files/folders with the system's default application is inherently
60
+ // platform-specific and cannot be implemented in pure Node.js.
61
+ // Each platform function contains the appropriate system call.
62
+ throw new Error(
63
+ 'do_o_nodejs should not be called directly. ' +
64
+ 'Opening files requires OS-specific commands.'
65
+ );
66
+ }
67
+
68
+ /**
69
+ * Validates and resolves the target path to open.
70
+ * Returns the resolved path or prints an error and exits.
71
+ *
72
+ * @param {string} targetPath - The path provided by the user
73
+ * @returns {string} The resolved absolute path
74
+ */
75
+ function validatePath(targetPath) {
76
+ // Resolve to absolute path
77
+ const absolutePath = path.resolve(targetPath);
78
+
79
+ // Check if the path exists
80
+ if (!fs.existsSync(absolutePath)) {
81
+ console.error(`Error: '${targetPath}' does not exist.`);
82
+ process.exit(1);
83
+ }
84
+
85
+ return absolutePath;
86
+ }
87
+
88
+ /**
89
+ * Open file or folder on macOS using the 'open' command.
90
+ *
91
+ * The 'open' command is built into macOS and opens files/folders with the
92
+ * default application. For directories, it opens Finder. For files, it
93
+ * opens the associated application based on the file's type and extension.
94
+ *
95
+ * Additional arguments supported by 'open' are passed through, allowing
96
+ * users to specify specific applications with -a, reveal in Finder with -R, etc.
97
+ *
98
+ * Usage examples:
99
+ * o # Open current directory in Finder
100
+ * o ~/Documents # Open Documents folder in Finder
101
+ * o file.pdf # Open PDF in default viewer
102
+ * o -a Safari url # Open URL in Safari specifically
103
+ * o -R file.txt # Reveal file in Finder without opening
104
+ *
105
+ * @param {string[]} args - Command line arguments passed to 'open'
106
+ * @returns {Promise<void>}
107
+ */
108
+ async function do_o_macos(args) {
109
+ // If no arguments provided, open the current directory
110
+ const target = args.length > 0 ? args.join(' ') : '.';
111
+
112
+ // If it's just a path (not flags), validate it exists
113
+ if (args.length === 0 || (args.length === 1 && !args[0].startsWith('-'))) {
114
+ const pathToOpen = args.length === 0 ? '.' : args[0];
115
+ validatePath(pathToOpen);
116
+ }
117
+
118
+ try {
119
+ // Use the native 'open' command
120
+ // Pass all arguments through to support flags like -a, -R, etc.
121
+ execSync(`open ${target}`, { stdio: 'inherit' });
122
+ } catch (error) {
123
+ console.error(`Error: Could not open '${target}'.`);
124
+ process.exit(1);
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Open file or folder on Ubuntu using xdg-open or available alternatives.
130
+ *
131
+ * On Linux, 'xdg-open' is the standard cross-desktop way to open files/URLs
132
+ * with the default application. It's part of the xdg-utils package and works
133
+ * with GNOME, KDE, XFCE, and other desktop environments.
134
+ *
135
+ * If xdg-open is not available, falls back to desktop-specific commands:
136
+ * - GNOME: gnome-open (deprecated), nautilus, gio open
137
+ * - KDE: kde-open, kde-open5, kioclient5
138
+ * - Others: sensible-browser (for URLs), exo-open (XFCE)
139
+ *
140
+ * @param {string[]} args - Command line arguments
141
+ * @param {string} [args[0]] - Path to open (defaults to current directory)
142
+ * @returns {Promise<void>}
143
+ */
144
+ async function do_o_ubuntu(args) {
145
+ // Check if we have a desktop environment available
146
+ const hasDesktop = os.isDesktopAvailable();
147
+
148
+ if (!hasDesktop) {
149
+ console.log('No desktop environment detected.');
150
+ console.log('This command requires a graphical environment to open files.');
151
+ console.log('');
152
+ console.log('On a headless server, consider using:');
153
+ console.log(' - cat, less, or vim to view file contents');
154
+ console.log(' - ls to list directory contents');
155
+ return;
156
+ }
157
+
158
+ // Get the target path (default to current directory)
159
+ const targetPath = args[0] || '.';
160
+ const absolutePath = validatePath(targetPath);
161
+
162
+ // List of open commands to try, in order of preference
163
+ const openCommands = [
164
+ { cmd: 'xdg-open', check: 'xdg-open' },
165
+ { cmd: 'gio', args: 'open', check: 'gio' },
166
+ { cmd: 'gnome-open', check: 'gnome-open' },
167
+ { cmd: 'kde-open5', check: 'kde-open5' },
168
+ { cmd: 'kde-open', check: 'kde-open' },
169
+ { cmd: 'exo-open', check: 'exo-open' },
170
+ // For directories specifically, try file managers
171
+ { cmd: 'nautilus', check: 'nautilus', dirOnly: true },
172
+ { cmd: 'dolphin', check: 'dolphin', dirOnly: true },
173
+ { cmd: 'thunar', check: 'thunar', dirOnly: true },
174
+ { cmd: 'pcmanfm', check: 'pcmanfm', dirOnly: true }
175
+ ];
176
+
177
+ const isDirectory = fs.statSync(absolutePath).isDirectory();
178
+
179
+ for (const openMethod of openCommands) {
180
+ // Skip directory-only commands for files
181
+ if (openMethod.dirOnly && !isDirectory) {
182
+ continue;
183
+ }
184
+
185
+ if (isCommandAvailable(openMethod.check)) {
186
+ try {
187
+ const fullCommand = openMethod.args
188
+ ? `${openMethod.cmd} ${openMethod.args} "${absolutePath}"`
189
+ : `${openMethod.cmd} "${absolutePath}"`;
190
+
191
+ // Use exec (non-blocking) so the GUI app can open without blocking the terminal
192
+ // Redirect stderr to /dev/null to suppress GTK warnings that don't affect functionality
193
+ exec(`${fullCommand} 2>/dev/null`, (error) => {
194
+ // Don't report errors - the command may return non-zero but still work
195
+ });
196
+ return; // Success, exit
197
+ } catch {
198
+ // This method failed, try the next one
199
+ continue;
200
+ }
201
+ }
202
+ }
203
+
204
+ // No open method worked
205
+ console.error('Error: Could not find a command to open files.');
206
+ console.error('');
207
+ console.error('Install xdg-utils for standard file opening support:');
208
+ console.error(' sudo apt install xdg-utils');
209
+ process.exit(1);
210
+ }
211
+
212
+ /**
213
+ * Open file or folder on Raspberry Pi OS.
214
+ *
215
+ * Raspberry Pi OS is Debian-based and typically uses LXDE or PIXEL desktop.
216
+ * The behavior is similar to Ubuntu, using xdg-open or desktop-specific
217
+ * commands as fallbacks.
218
+ *
219
+ * @param {string[]} args - Command line arguments
220
+ * @param {string} [args[0]] - Path to open (defaults to current directory)
221
+ * @returns {Promise<void>}
222
+ */
223
+ async function do_o_raspbian(args) {
224
+ // Check if we have a desktop environment available
225
+ const hasDesktop = os.isDesktopAvailable();
226
+
227
+ if (!hasDesktop) {
228
+ console.log('No desktop environment detected.');
229
+ console.log('This command requires a graphical environment to open files.');
230
+ console.log('');
231
+ console.log('On a headless Raspberry Pi, consider using:');
232
+ console.log(' - cat, less, or vim to view file contents');
233
+ console.log(' - ls to list directory contents');
234
+ return;
235
+ }
236
+
237
+ // Get the target path (default to current directory)
238
+ const targetPath = args[0] || '.';
239
+ const absolutePath = validatePath(targetPath);
240
+
241
+ // List of open commands to try, in order of preference for Raspberry Pi
242
+ const openCommands = [
243
+ { cmd: 'xdg-open', check: 'xdg-open' },
244
+ // PCManFM is the default file manager on Raspberry Pi OS with LXDE
245
+ { cmd: 'pcmanfm', check: 'pcmanfm', dirOnly: true },
246
+ { cmd: 'gio', args: 'open', check: 'gio' },
247
+ { cmd: 'exo-open', check: 'exo-open' },
248
+ { cmd: 'thunar', check: 'thunar', dirOnly: true }
249
+ ];
250
+
251
+ const isDirectory = fs.statSync(absolutePath).isDirectory();
252
+
253
+ for (const openMethod of openCommands) {
254
+ // Skip directory-only commands for files
255
+ if (openMethod.dirOnly && !isDirectory) {
256
+ continue;
257
+ }
258
+
259
+ if (isCommandAvailable(openMethod.check)) {
260
+ try {
261
+ const fullCommand = openMethod.args
262
+ ? `${openMethod.cmd} ${openMethod.args} "${absolutePath}"`
263
+ : `${openMethod.cmd} "${absolutePath}"`;
264
+
265
+ // Use exec (non-blocking) so the GUI app can open without blocking the terminal
266
+ exec(`${fullCommand} 2>/dev/null`, (error) => {
267
+ // Don't report errors - the command may return non-zero but still work
268
+ });
269
+ return; // Success, exit
270
+ } catch {
271
+ // This method failed, try the next one
272
+ continue;
273
+ }
274
+ }
275
+ }
276
+
277
+ // No open method worked
278
+ console.error('Error: Could not find a command to open files.');
279
+ console.error('');
280
+ console.error('Install xdg-utils for standard file opening support:');
281
+ console.error(' sudo apt install xdg-utils');
282
+ process.exit(1);
283
+ }
284
+
285
+ /**
286
+ * Open file or folder on Amazon Linux.
287
+ *
288
+ * Amazon Linux is typically used in server environments without a desktop.
289
+ * If a desktop is present, it attempts to use xdg-open or available alternatives.
290
+ *
291
+ * @param {string[]} args - Command line arguments
292
+ * @param {string} [args[0]] - Path to open (defaults to current directory)
293
+ * @returns {Promise<void>}
294
+ */
295
+ async function do_o_amazon_linux(args) {
296
+ // Check if we have a desktop environment available
297
+ const hasDesktop = os.isDesktopAvailable();
298
+
299
+ if (!hasDesktop) {
300
+ console.log('No desktop environment detected.');
301
+ console.log('Amazon Linux is typically used in server environments.');
302
+ console.log('');
303
+ console.log('On a headless server, consider using:');
304
+ console.log(' - cat, less, or vim to view file contents');
305
+ console.log(' - ls to list directory contents');
306
+ return;
307
+ }
308
+
309
+ // Get the target path (default to current directory)
310
+ const targetPath = args[0] || '.';
311
+ const absolutePath = validatePath(targetPath);
312
+
313
+ // Try available open commands
314
+ const openCommands = [
315
+ { cmd: 'xdg-open', check: 'xdg-open' },
316
+ { cmd: 'gio', args: 'open', check: 'gio' }
317
+ ];
318
+
319
+ for (const openMethod of openCommands) {
320
+ if (isCommandAvailable(openMethod.check)) {
321
+ try {
322
+ const fullCommand = openMethod.args
323
+ ? `${openMethod.cmd} ${openMethod.args} "${absolutePath}"`
324
+ : `${openMethod.cmd} "${absolutePath}"`;
325
+
326
+ exec(`${fullCommand} 2>/dev/null`, (error) => {
327
+ // Don't report errors
328
+ });
329
+ return;
330
+ } catch {
331
+ continue;
332
+ }
333
+ }
334
+ }
335
+
336
+ console.error('Error: Could not find a command to open files.');
337
+ console.error('');
338
+ console.error('Install xdg-utils for standard file opening support:');
339
+ console.error(' sudo dnf install xdg-utils');
340
+ process.exit(1);
341
+ }
342
+
8
343
  /**
9
- * Opens the specified file or folder in the system's default file manager.
10
- * Uses platform-specific commands (macOS: open, Linux: xdg-open, Windows: explorer).
344
+ * Open file or folder in Windows Command Prompt using 'start' command.
345
+ *
346
+ * The 'start' command is built into Windows CMD and opens files/folders with
347
+ * the default application. For directories, it opens Explorer. For files, it
348
+ * opens the associated application.
349
+ *
350
+ * Note: The 'start' command has special quoting requirements. When the path
351
+ * contains spaces and is quoted, 'start' interprets it as a window title.
352
+ * We use an empty title ("") to avoid this issue.
11
353
  *
12
354
  * @param {string[]} args - Command line arguments
13
- * @param {string} [args.0] - Path to open (defaults to current directory)
355
+ * @param {string} [args[0]] - Path to open (defaults to current directory)
14
356
  * @returns {Promise<void>}
15
357
  */
16
- async function main(args) {
17
- // TODO: Implement file manager open
358
+ async function do_o_cmd(args) {
359
+ // Get the target path (default to current directory)
360
+ const targetPath = args[0] || '.';
361
+ const absolutePath = validatePath(targetPath);
362
+
363
+ try {
364
+ // The 'start' command needs an empty title ("") before the path if the path contains spaces
365
+ // Otherwise it interprets the quoted path as a window title
366
+ // Use cmd /c to run the command properly
367
+ execSync(`cmd /c start "" "${absolutePath}"`, { stdio: 'inherit' });
368
+ } catch (error) {
369
+ console.error(`Error: Could not open '${targetPath}'.`);
370
+ process.exit(1);
371
+ }
372
+ }
373
+
374
+ /**
375
+ * Open file or folder in Windows PowerShell using Start-Process or Invoke-Item.
376
+ *
377
+ * PowerShell provides several ways to open files:
378
+ * - Invoke-Item: Opens items with the default application
379
+ * - Start-Process: More control over how the process is started
380
+ * - explorer.exe: Directly calls Windows Explorer
381
+ *
382
+ * We use Invoke-Item as it's the most natural PowerShell equivalent of 'open'.
383
+ *
384
+ * @param {string[]} args - Command line arguments
385
+ * @param {string} [args[0]] - Path to open (defaults to current directory)
386
+ * @returns {Promise<void>}
387
+ */
388
+ async function do_o_powershell(args) {
389
+ // Get the target path (default to current directory)
390
+ const targetPath = args[0] || '.';
391
+ const absolutePath = validatePath(targetPath);
392
+
393
+ try {
394
+ // Invoke-Item opens the item with its default application
395
+ // Escape the path for PowerShell
396
+ const escapedPath = absolutePath.replace(/'/g, "''");
397
+ execSync(`powershell -Command "Invoke-Item -Path '${escapedPath}'"`, { stdio: 'inherit' });
398
+ } catch (error) {
399
+ // Fallback to explorer.exe if Invoke-Item fails
400
+ try {
401
+ execSync(`explorer.exe "${absolutePath}"`, { stdio: 'inherit' });
402
+ } catch {
403
+ console.error(`Error: Could not open '${targetPath}'.`);
404
+ process.exit(1);
405
+ }
406
+ }
407
+ }
408
+
409
+ /**
410
+ * Open file or folder from Git Bash on Windows.
411
+ *
412
+ * Git Bash runs on Windows, so we can use Windows commands to open files.
413
+ * The 'start' command is available in Git Bash through MSYS2/MinGW.
414
+ * Alternatively, we can call explorer.exe directly.
415
+ *
416
+ * @param {string[]} args - Command line arguments
417
+ * @param {string} [args[0]] - Path to open (defaults to current directory)
418
+ * @returns {Promise<void>}
419
+ */
420
+ async function do_o_gitbash(args) {
421
+ // Get the target path (default to current directory)
422
+ const targetPath = args[0] || '.';
423
+ const absolutePath = validatePath(targetPath);
424
+
425
+ try {
426
+ // Use start command which is available in Git Bash
427
+ // Convert path to Windows format if needed
428
+ execSync(`start "" "${absolutePath}"`, { stdio: 'inherit' });
429
+ } catch {
430
+ // Fallback to explorer.exe
431
+ try {
432
+ execSync(`explorer.exe "${absolutePath}"`, { stdio: 'inherit' });
433
+ } catch {
434
+ console.error(`Error: Could not open '${targetPath}'.`);
435
+ process.exit(1);
436
+ }
437
+ }
438
+ }
439
+
440
+ /**
441
+ * Main entry point - detects environment and executes appropriate implementation.
442
+ *
443
+ * The "o" (open) command opens a file or folder using the operating system's
444
+ * default application. This is a quick shortcut for opening things from the
445
+ * command line without typing the full command name:
446
+ *
447
+ * - On macOS: Opens with Finder for directories, default app for files
448
+ * - On Linux: Uses xdg-open or desktop-specific commands
449
+ * - On Windows: Uses start/explorer to open with default application
450
+ *
451
+ * Usage:
452
+ * o # Open current directory in file manager
453
+ * o ~/Documents # Open Documents folder
454
+ * o file.pdf # Open PDF in default viewer
455
+ * o image.png # Open image in default viewer
456
+ * o http://example.com # Open URL in default browser (macOS/Linux)
457
+ *
458
+ * @param {string[]} args - Command line arguments
459
+ * @returns {Promise<void>}
460
+ */
461
+ async function do_o(args) {
462
+ const platform = os.detect();
463
+
464
+ const handlers = {
465
+ 'macos': do_o_macos,
466
+ 'ubuntu': do_o_ubuntu,
467
+ 'debian': do_o_ubuntu,
468
+ 'raspbian': do_o_raspbian,
469
+ 'amazon_linux': do_o_amazon_linux,
470
+ 'rhel': do_o_amazon_linux,
471
+ 'fedora': do_o_ubuntu,
472
+ 'linux': do_o_ubuntu,
473
+ 'wsl': do_o_ubuntu,
474
+ 'cmd': do_o_cmd,
475
+ 'windows': do_o_cmd,
476
+ 'powershell': do_o_powershell,
477
+ 'gitbash': do_o_gitbash
478
+ };
479
+
480
+ const handler = handlers[platform.type];
481
+ if (!handler) {
482
+ console.error(`Platform '${platform.type}' is not supported for this command.`);
483
+ console.error('');
484
+ console.error('Supported platforms:');
485
+ console.error(' - macOS');
486
+ console.error(' - Ubuntu, Debian, and other Linux distributions');
487
+ console.error(' - Raspberry Pi OS');
488
+ console.error(' - Amazon Linux');
489
+ console.error(' - Windows (CMD, PowerShell, Git Bash)');
490
+ process.exit(1);
491
+ }
492
+
493
+ await handler(args);
18
494
  }
19
495
 
20
- module.exports = { main };
496
+ module.exports = {
497
+ main: do_o,
498
+ do_o,
499
+ do_o_nodejs,
500
+ do_o_macos,
501
+ do_o_ubuntu,
502
+ do_o_raspbian,
503
+ do_o_amazon_linux,
504
+ do_o_cmd,
505
+ do_o_powershell,
506
+ do_o_gitbash
507
+ };
21
508
 
22
509
  if (require.main === module) {
23
- main(process.argv.slice(2));
510
+ do_o(process.argv.slice(2));
24
511
  }