@fredlackey/devutils 0.0.18 → 0.1.0
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.
- package/README.md +214 -141
- package/package.json +8 -83
- package/src/api/loader.js +229 -0
- package/src/api/registry.json +62 -0
- package/src/cli.js +293 -60
- package/src/commands/ai/index.js +16 -0
- package/src/commands/ai/launch.js +112 -0
- package/src/commands/ai/list.js +54 -0
- package/src/commands/ai/resume.js +70 -0
- package/src/commands/ai/sessions.js +121 -0
- package/src/commands/ai/set.js +131 -0
- package/src/commands/ai/show.js +74 -0
- package/src/commands/ai/tools.js +46 -0
- package/src/commands/alias/add.js +93 -0
- package/src/commands/alias/helpers.js +107 -0
- package/src/commands/alias/index.js +14 -0
- package/src/commands/alias/list.js +55 -0
- package/src/commands/alias/remove.js +62 -0
- package/src/commands/alias/sync.js +109 -0
- package/src/commands/api/disable.js +73 -0
- package/src/commands/api/enable.js +148 -0
- package/src/commands/api/index.js +15 -0
- package/src/commands/api/list.js +66 -0
- package/src/commands/api/update.js +87 -0
- package/src/commands/auth/index.js +15 -0
- package/src/commands/auth/list.js +49 -0
- package/src/commands/auth/login.js +384 -0
- package/src/commands/auth/logout.js +111 -0
- package/src/commands/auth/refresh.js +184 -0
- package/src/commands/auth/services.js +169 -0
- package/src/commands/auth/status.js +104 -0
- package/src/commands/config/export.js +224 -0
- package/src/commands/config/get.js +52 -0
- package/src/commands/config/import.js +308 -0
- package/src/commands/config/index.js +17 -0
- package/src/commands/config/init.js +143 -0
- package/src/commands/config/reset.js +57 -0
- package/src/commands/config/set.js +93 -0
- package/src/commands/config/show.js +35 -0
- package/src/commands/help.js +338 -0
- package/src/commands/identity/add.js +133 -0
- package/src/commands/identity/index.js +17 -0
- package/src/commands/identity/link.js +76 -0
- package/src/commands/identity/list.js +48 -0
- package/src/commands/identity/remove.js +72 -0
- package/src/commands/identity/show.js +65 -0
- package/src/commands/identity/sync.js +172 -0
- package/src/commands/identity/unlink.js +57 -0
- package/src/commands/ignore/add.js +165 -0
- package/src/commands/ignore/index.js +14 -0
- package/src/commands/ignore/list.js +89 -0
- package/src/commands/ignore/markers.js +43 -0
- package/src/commands/ignore/remove.js +164 -0
- package/src/commands/ignore/show.js +169 -0
- package/src/commands/machine/detect.js +122 -0
- package/src/commands/machine/index.js +14 -0
- package/src/commands/machine/list.js +74 -0
- package/src/commands/machine/set.js +106 -0
- package/src/commands/machine/show.js +35 -0
- package/src/commands/schema.js +152 -0
- package/src/commands/search/collections.js +134 -0
- package/src/commands/search/get.js +71 -0
- package/src/commands/search/index-cmd.js +54 -0
- package/src/commands/search/index.js +21 -0
- package/src/commands/search/keyword.js +60 -0
- package/src/commands/search/qmd.js +70 -0
- package/src/commands/search/query.js +64 -0
- package/src/commands/search/semantic.js +62 -0
- package/src/commands/search/status.js +46 -0
- package/src/commands/status.js +224 -171
- package/src/commands/tools/check.js +79 -0
- package/src/commands/tools/index.js +14 -0
- package/src/commands/tools/install.js +110 -0
- package/src/commands/tools/list.js +91 -0
- package/src/commands/tools/search.js +60 -0
- package/src/commands/update.js +83 -112
- package/src/commands/util/add.js +151 -0
- package/src/commands/util/index.js +15 -0
- package/src/commands/util/list.js +97 -0
- package/src/commands/util/remove.js +76 -0
- package/src/commands/util/run.js +79 -0
- package/src/commands/util/show.js +67 -0
- package/src/commands/version.js +21 -88
- package/src/installers/_template.js +104 -0
- package/src/installers/git.js +150 -0
- package/src/installers/homebrew.js +190 -0
- package/src/installers/node.js +223 -0
- package/src/installers/registry.json +29 -0
- package/src/lib/config.js +125 -0
- package/src/lib/detect.js +74 -0
- package/src/lib/errors.js +114 -0
- package/src/lib/github.js +315 -0
- package/src/lib/installer.js +225 -0
- package/src/lib/output.js +239 -0
- package/src/lib/platform.js +112 -0
- package/src/lib/platforms/amazon-linux.js +41 -0
- package/src/lib/platforms/gitbash.js +46 -0
- package/src/lib/platforms/macos.js +45 -0
- package/src/lib/platforms/raspbian.js +41 -0
- package/src/lib/platforms/ubuntu.js +39 -0
- package/src/lib/platforms/windows.js +45 -0
- package/src/lib/prompt.js +161 -0
- package/src/lib/schema.js +211 -0
- package/src/lib/shell.js +75 -0
- package/src/patterns/gitignore/claude-code.txt +25 -0
- package/src/patterns/gitignore/docker.txt +15 -0
- package/src/patterns/gitignore/go.txt +24 -0
- package/src/patterns/gitignore/java.txt +38 -0
- package/src/patterns/gitignore/jetbrains.txt +26 -0
- package/src/patterns/gitignore/linux.txt +18 -0
- package/src/patterns/gitignore/macos.txt +27 -0
- package/src/patterns/gitignore/node.txt +51 -0
- package/src/patterns/gitignore/python.txt +55 -0
- package/src/patterns/gitignore/rust.txt +14 -0
- package/src/patterns/gitignore/terraform.txt +30 -0
- package/src/patterns/gitignore/vscode.txt +15 -0
- package/src/patterns/gitignore/windows.txt +25 -0
- package/src/utils/clone/index.js +165 -0
- package/src/utils/git-push/index.js +230 -0
- package/src/utils/git-status/index.js +116 -0
- package/src/utils/git-status/unix.sh +75 -0
- package/src/utils/registry.json +41 -0
- package/bin/dev.js +0 -16
- package/files/README.md +0 -0
- package/files/claude/.claude/commands/setup-context.md +0 -3
- package/files/monorepos/_archive/README.md +0 -36
- package/files/monorepos/_legacy/README.md +0 -36
- package/files/monorepos/ai-docs/README.md +0 -33
- package/files/monorepos/apps/README.md +0 -24
- package/files/monorepos/docs/README.md +0 -40
- package/files/monorepos/packages/README.md +0 -25
- package/files/monorepos/research/README.md +0 -29
- package/files/monorepos/scripts/README.md +0 -24
- package/src/commands/README.md +0 -41
- package/src/commands/configure.js +0 -199
- package/src/commands/identity.js +0 -1630
- package/src/commands/ignore.js +0 -247
- package/src/commands/install.js +0 -526
- package/src/commands/setup.js +0 -246
- package/src/completion.js +0 -284
- package/src/constants.js +0 -45
- package/src/ignore/claude-code.txt +0 -10
- package/src/ignore/docker.txt +0 -18
- package/src/ignore/linux.txt +0 -23
- package/src/ignore/macos.txt +0 -36
- package/src/ignore/node.txt +0 -55
- package/src/ignore/terraform.txt +0 -37
- package/src/ignore/vscode.txt +0 -18
- package/src/ignore/windows.txt +0 -35
- package/src/index.js +0 -0
- package/src/installs/README.md +0 -399
- package/src/installs/adobe-creative-cloud.js +0 -546
- package/src/installs/adobe-creative-cloud.md +0 -605
- package/src/installs/appcleaner.js +0 -321
- package/src/installs/appcleaner.md +0 -699
- package/src/installs/apt-transport-https.js +0 -390
- package/src/installs/apt-transport-https.md +0 -678
- package/src/installs/atomicparsley.js +0 -642
- package/src/installs/atomicparsley.md +0 -795
- package/src/installs/aws-cli.js +0 -797
- package/src/installs/aws-cli.md +0 -727
- package/src/installs/balena-etcher.js +0 -710
- package/src/installs/balena-etcher.md +0 -761
- package/src/installs/bambu-studio.js +0 -1143
- package/src/installs/bambu-studio.md +0 -780
- package/src/installs/bash-completion.js +0 -575
- package/src/installs/bash-completion.md +0 -833
- package/src/installs/bash.js +0 -417
- package/src/installs/bash.md +0 -993
- package/src/installs/beyond-compare.js +0 -603
- package/src/installs/beyond-compare.md +0 -813
- package/src/installs/brave-browser.js +0 -968
- package/src/installs/brave-browser.md +0 -650
- package/src/installs/build-essential.js +0 -529
- package/src/installs/build-essential.md +0 -977
- package/src/installs/ca-certificates.js +0 -618
- package/src/installs/ca-certificates.md +0 -937
- package/src/installs/caffeine.js +0 -508
- package/src/installs/caffeine.md +0 -839
- package/src/installs/camtasia.js +0 -596
- package/src/installs/camtasia.md +0 -762
- package/src/installs/chatgpt.js +0 -476
- package/src/installs/chatgpt.md +0 -814
- package/src/installs/chocolatey.js +0 -456
- package/src/installs/chocolatey.md +0 -661
- package/src/installs/chrome-canary.js +0 -419
- package/src/installs/chrome-canary.md +0 -641
- package/src/installs/chromium.js +0 -667
- package/src/installs/chromium.md +0 -838
- package/src/installs/claude-code.js +0 -576
- package/src/installs/claude-code.md +0 -1173
- package/src/installs/cloudflare-warp.js +0 -900
- package/src/installs/cloudflare-warp.md +0 -1047
- package/src/installs/comet-browser.js +0 -588
- package/src/installs/comet-browser.md +0 -731
- package/src/installs/curl.js +0 -379
- package/src/installs/curl.md +0 -714
- package/src/installs/cursor.js +0 -579
- package/src/installs/cursor.md +0 -970
- package/src/installs/dbeaver.js +0 -924
- package/src/installs/dbeaver.md +0 -939
- package/src/installs/dbschema.js +0 -692
- package/src/installs/dbschema.md +0 -925
- package/src/installs/dependencies.md +0 -453
- package/src/installs/development-tools.js +0 -600
- package/src/installs/development-tools.md +0 -977
- package/src/installs/docker.js +0 -1029
- package/src/installs/docker.md +0 -1109
- package/src/installs/drawio.js +0 -1019
- package/src/installs/drawio.md +0 -795
- package/src/installs/elmedia-player.js +0 -347
- package/src/installs/elmedia-player.md +0 -556
- package/src/installs/ffmpeg.js +0 -889
- package/src/installs/ffmpeg.md +0 -852
- package/src/installs/file.js +0 -464
- package/src/installs/file.md +0 -987
- package/src/installs/gemini-cli.js +0 -811
- package/src/installs/gemini-cli.md +0 -1153
- package/src/installs/git.js +0 -400
- package/src/installs/git.md +0 -907
- package/src/installs/gitego.js +0 -949
- package/src/installs/gitego.md +0 -1172
- package/src/installs/go.js +0 -931
- package/src/installs/go.md +0 -958
- package/src/installs/google-antigravity.js +0 -913
- package/src/installs/google-antigravity.md +0 -1075
- package/src/installs/google-chrome.js +0 -833
- package/src/installs/google-chrome.md +0 -862
- package/src/installs/gpg.js +0 -480
- package/src/installs/gpg.md +0 -1056
- package/src/installs/homebrew.js +0 -1028
- package/src/installs/homebrew.md +0 -988
- package/src/installs/imageoptim.js +0 -968
- package/src/installs/imageoptim.md +0 -1119
- package/src/installs/installers.json +0 -4032
- package/src/installs/installers.json.tmp +0 -3953
- package/src/installs/jq.js +0 -400
- package/src/installs/jq.md +0 -809
- package/src/installs/keyboard-maestro.js +0 -719
- package/src/installs/keyboard-maestro.md +0 -825
- package/src/installs/kiro.js +0 -864
- package/src/installs/kiro.md +0 -1015
- package/src/installs/latex.js +0 -789
- package/src/installs/latex.md +0 -1095
- package/src/installs/lftp.js +0 -356
- package/src/installs/lftp.md +0 -907
- package/src/installs/lsb-release.js +0 -346
- package/src/installs/lsb-release.md +0 -814
- package/src/installs/messenger.js +0 -847
- package/src/installs/messenger.md +0 -900
- package/src/installs/microsoft-office.js +0 -568
- package/src/installs/microsoft-office.md +0 -760
- package/src/installs/microsoft-teams.js +0 -801
- package/src/installs/microsoft-teams.md +0 -886
- package/src/installs/moom.js +0 -326
- package/src/installs/moom.md +0 -570
- package/src/installs/node.js +0 -904
- package/src/installs/node.md +0 -1153
- package/src/installs/nordpass.js +0 -716
- package/src/installs/nordpass.md +0 -921
- package/src/installs/nordvpn.js +0 -892
- package/src/installs/nordvpn.md +0 -1052
- package/src/installs/nvm.js +0 -995
- package/src/installs/nvm.md +0 -1057
- package/src/installs/ohmyzsh.js +0 -529
- package/src/installs/ohmyzsh.md +0 -1094
- package/src/installs/openssh.js +0 -804
- package/src/installs/openssh.md +0 -1056
- package/src/installs/pandoc.js +0 -662
- package/src/installs/pandoc.md +0 -1036
- package/src/installs/parallels-desktop.js +0 -431
- package/src/installs/parallels-desktop.md +0 -446
- package/src/installs/pinentry.js +0 -510
- package/src/installs/pinentry.md +0 -1142
- package/src/installs/pngyu.js +0 -869
- package/src/installs/pngyu.md +0 -896
- package/src/installs/postman.js +0 -799
- package/src/installs/postman.md +0 -940
- package/src/installs/procps.js +0 -425
- package/src/installs/procps.md +0 -851
- package/src/installs/safari-tech-preview.js +0 -374
- package/src/installs/safari-tech-preview.md +0 -533
- package/src/installs/sfnt2woff.js +0 -658
- package/src/installs/sfnt2woff.md +0 -795
- package/src/installs/shellcheck.js +0 -481
- package/src/installs/shellcheck.md +0 -1005
- package/src/installs/slack.js +0 -741
- package/src/installs/slack.md +0 -865
- package/src/installs/snagit.js +0 -585
- package/src/installs/snagit.md +0 -844
- package/src/installs/software-properties-common.js +0 -372
- package/src/installs/software-properties-common.md +0 -805
- package/src/installs/spotify.js +0 -877
- package/src/installs/spotify.md +0 -901
- package/src/installs/studio-3t.js +0 -823
- package/src/installs/studio-3t.md +0 -918
- package/src/installs/sublime-text.js +0 -804
- package/src/installs/sublime-text.md +0 -914
- package/src/installs/superwhisper.js +0 -706
- package/src/installs/superwhisper.md +0 -630
- package/src/installs/tailscale.js +0 -745
- package/src/installs/tailscale.md +0 -1100
- package/src/installs/tar.js +0 -389
- package/src/installs/tar.md +0 -946
- package/src/installs/termius.js +0 -798
- package/src/installs/termius.md +0 -844
- package/src/installs/terraform.js +0 -779
- package/src/installs/terraform.md +0 -899
- package/src/installs/tfenv.js +0 -778
- package/src/installs/tfenv.md +0 -1091
- package/src/installs/tidal.js +0 -771
- package/src/installs/tidal.md +0 -864
- package/src/installs/tmux.js +0 -346
- package/src/installs/tmux.md +0 -1030
- package/src/installs/tree.js +0 -411
- package/src/installs/tree.md +0 -833
- package/src/installs/unzip.js +0 -460
- package/src/installs/unzip.md +0 -879
- package/src/installs/vim.js +0 -421
- package/src/installs/vim.md +0 -1040
- package/src/installs/vlc.js +0 -821
- package/src/installs/vlc.md +0 -927
- package/src/installs/vscode.js +0 -843
- package/src/installs/vscode.md +0 -1002
- package/src/installs/wget.js +0 -420
- package/src/installs/wget.md +0 -791
- package/src/installs/whatsapp.js +0 -729
- package/src/installs/whatsapp.md +0 -854
- package/src/installs/winpty.js +0 -352
- package/src/installs/winpty.md +0 -620
- package/src/installs/woff2.js +0 -553
- package/src/installs/woff2.md +0 -977
- package/src/installs/wsl.js +0 -572
- package/src/installs/wsl.md +0 -699
- package/src/installs/xcode-clt.js +0 -520
- package/src/installs/xcode-clt.md +0 -351
- package/src/installs/xcode.js +0 -560
- package/src/installs/xcode.md +0 -573
- package/src/installs/yarn.js +0 -824
- package/src/installs/yarn.md +0 -1074
- package/src/installs/yq.js +0 -654
- package/src/installs/yq.md +0 -944
- package/src/installs/yt-dlp.js +0 -701
- package/src/installs/yt-dlp.md +0 -946
- package/src/installs/yum-utils.js +0 -297
- package/src/installs/yum-utils.md +0 -648
- package/src/installs/zoom.js +0 -759
- package/src/installs/zoom.md +0 -884
- package/src/installs/zsh.js +0 -455
- package/src/installs/zsh.md +0 -1008
- package/src/scripts/README.md +0 -617
- package/src/scripts/STATUS.md +0 -208
- package/src/scripts/afk.js +0 -411
- package/src/scripts/backup-all.js +0 -746
- package/src/scripts/backup-source.js +0 -727
- package/src/scripts/brewd.js +0 -389
- package/src/scripts/brewi.js +0 -520
- package/src/scripts/brewr.js +0 -527
- package/src/scripts/brews.js +0 -477
- package/src/scripts/brewu.js +0 -504
- package/src/scripts/c.js +0 -201
- package/src/scripts/ccurl.js +0 -341
- package/src/scripts/certbot-crontab-init.js +0 -504
- package/src/scripts/certbot-init.js +0 -657
- package/src/scripts/ch.js +0 -355
- package/src/scripts/claude-danger.js +0 -268
- package/src/scripts/clean-dev.js +0 -435
- package/src/scripts/clear-dns-cache.js +0 -541
- package/src/scripts/clone.js +0 -435
- package/src/scripts/code-all.js +0 -437
- package/src/scripts/count-files.js +0 -211
- package/src/scripts/count-folders.js +0 -211
- package/src/scripts/count.js +0 -264
- package/src/scripts/d.js +0 -219
- package/src/scripts/datauri.js +0 -389
- package/src/scripts/delete-files.js +0 -380
- package/src/scripts/docker-clean.js +0 -426
- package/src/scripts/dp.js +0 -442
- package/src/scripts/e.js +0 -390
- package/src/scripts/empty-trash.js +0 -513
- package/src/scripts/evm.js +0 -444
- package/src/scripts/fetch-github-repos.js +0 -456
- package/src/scripts/get-channel.js +0 -345
- package/src/scripts/get-course.js +0 -399
- package/src/scripts/get-dependencies.js +0 -306
- package/src/scripts/get-folder.js +0 -799
- package/src/scripts/get-tunes.js +0 -426
- package/src/scripts/get-video.js +0 -367
- package/src/scripts/git-backup.js +0 -577
- package/src/scripts/git-clone.js +0 -493
- package/src/scripts/git-pup.js +0 -319
- package/src/scripts/git-push.js +0 -396
- package/src/scripts/h.js +0 -622
- package/src/scripts/hide-desktop-icons.js +0 -499
- package/src/scripts/hide-hidden-files.js +0 -538
- package/src/scripts/install-dependencies-from.js +0 -456
- package/src/scripts/ips.js +0 -663
- package/src/scripts/iso.js +0 -370
- package/src/scripts/killni.js +0 -577
- package/src/scripts/ll.js +0 -467
- package/src/scripts/local-ip.js +0 -325
- package/src/scripts/m.js +0 -524
- package/src/scripts/map.js +0 -309
- package/src/scripts/mkd.js +0 -351
- package/src/scripts/ncu-update-all.js +0 -457
- package/src/scripts/nginx-init.js +0 -718
- package/src/scripts/npmi.js +0 -382
- package/src/scripts/o.js +0 -511
- package/src/scripts/org-by-date.js +0 -338
- package/src/scripts/p.js +0 -224
- package/src/scripts/packages.js +0 -330
- package/src/scripts/path.js +0 -225
- package/src/scripts/ports.js +0 -597
- package/src/scripts/q.js +0 -305
- package/src/scripts/refresh-files.js +0 -394
- package/src/scripts/remove-smaller-files.js +0 -516
- package/src/scripts/rename-files-with-date.js +0 -533
- package/src/scripts/resize-image.js +0 -539
- package/src/scripts/rm-safe.js +0 -669
- package/src/scripts/s.js +0 -540
- package/src/scripts/set-git-public.js +0 -365
- package/src/scripts/show-desktop-icons.js +0 -475
- package/src/scripts/show-hidden-files.js +0 -472
- package/src/scripts/tpa.js +0 -280
- package/src/scripts/tpo.js +0 -280
- package/src/scripts/u.js +0 -505
- package/src/scripts/vpush.js +0 -437
- package/src/scripts/y.js +0 -283
- package/src/utils/README.md +0 -95
- package/src/utils/common/apps.js +0 -143
- package/src/utils/common/display.js +0 -157
- package/src/utils/common/network.js +0 -185
- package/src/utils/common/os.js +0 -294
- package/src/utils/common/package-manager.js +0 -301
- package/src/utils/common/privileges.js +0 -138
- package/src/utils/common/shell.js +0 -261
- package/src/utils/macos/apps.js +0 -228
- package/src/utils/macos/brew.js +0 -315
- package/src/utils/ubuntu/apt.js +0 -307
- package/src/utils/ubuntu/desktop.js +0 -292
- package/src/utils/ubuntu/snap.js +0 -344
- package/src/utils/ubuntu/systemd.js +0 -286
- package/src/utils/windows/choco.js +0 -465
- package/src/utils/windows/env.js +0 -246
- package/src/utils/windows/registry.js +0 -269
- package/src/utils/windows/shell.js +0 -240
- package/src/utils/windows/winget.js +0 -489
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Formats a byte count into a human-readable string (e.g., "16.0 GB").
|
|
9
|
+
* Rounds to one decimal place for clarity.
|
|
10
|
+
*
|
|
11
|
+
* @param {number} bytes - The number of bytes to format.
|
|
12
|
+
* @returns {string} A human-readable size string like "16.0 GB".
|
|
13
|
+
*/
|
|
14
|
+
function formatBytes(bytes) {
|
|
15
|
+
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
16
|
+
let i = 0;
|
|
17
|
+
let value = bytes;
|
|
18
|
+
while (value >= 1024 && i < units.length - 1) {
|
|
19
|
+
value /= 1024;
|
|
20
|
+
i++;
|
|
21
|
+
}
|
|
22
|
+
return `${value.toFixed(1)} ${units[i]}`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const meta = {
|
|
26
|
+
description: 'Detect the current machine\'s OS, architecture, package managers, and capabilities.',
|
|
27
|
+
arguments: [],
|
|
28
|
+
flags: []
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Detects the current machine's hardware and OS details, then writes
|
|
33
|
+
* the profile to ~/.devutils/machines/current.json.
|
|
34
|
+
*
|
|
35
|
+
* Uses context.platform.detect() for OS type, then layers on hostname,
|
|
36
|
+
* CPU count, memory, available package managers, and OS version info
|
|
37
|
+
* from Node.js built-in modules and shell commands.
|
|
38
|
+
*
|
|
39
|
+
* @param {object} args - Parsed command arguments (none expected).
|
|
40
|
+
* @param {object} context - The CLI context object with platform, shell, output, errors.
|
|
41
|
+
*/
|
|
42
|
+
async function run(args, context) {
|
|
43
|
+
// Step 1: Get base platform detection
|
|
44
|
+
const platform = context.platform.detect();
|
|
45
|
+
|
|
46
|
+
// Step 2: Gather hardware and system info from Node.js os module
|
|
47
|
+
const hostname = os.hostname();
|
|
48
|
+
const cpuCount = os.cpus().length;
|
|
49
|
+
const totalMemory = os.totalmem();
|
|
50
|
+
const arch = os.arch();
|
|
51
|
+
const osRelease = os.release();
|
|
52
|
+
|
|
53
|
+
// Step 3: Detect which package managers are installed on this machine
|
|
54
|
+
const packageManagers = [];
|
|
55
|
+
const candidates = ['brew', 'apt', 'snap', 'dnf', 'yum', 'choco', 'winget', 'npm', 'yarn', 'pnpm'];
|
|
56
|
+
|
|
57
|
+
for (const pm of candidates) {
|
|
58
|
+
if (context.shell.commandExists(pm)) {
|
|
59
|
+
packageManagers.push(pm);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Step 4: Get OS-specific version and name info
|
|
64
|
+
let osVersion = osRelease;
|
|
65
|
+
let osName = platform.type;
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
if (platform.type === 'macos') {
|
|
69
|
+
const result = await context.shell.exec('sw_vers -productVersion');
|
|
70
|
+
if (result.exitCode === 0 && result.stdout) {
|
|
71
|
+
osVersion = result.stdout.trim();
|
|
72
|
+
}
|
|
73
|
+
osName = 'macOS';
|
|
74
|
+
} else if (['ubuntu', 'raspbian', 'amazon-linux'].includes(platform.type)) {
|
|
75
|
+
const releaseFile = fs.readFileSync('/etc/os-release', 'utf8');
|
|
76
|
+
const nameMatch = releaseFile.match(/^PRETTY_NAME="?(.+?)"?$/m);
|
|
77
|
+
if (nameMatch) {
|
|
78
|
+
osName = nameMatch[1];
|
|
79
|
+
}
|
|
80
|
+
const versionMatch = releaseFile.match(/^VERSION_ID="?(.+?)"?$/m);
|
|
81
|
+
if (versionMatch) {
|
|
82
|
+
osVersion = versionMatch[1];
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
} catch (err) {
|
|
86
|
+
// Keep defaults if detection fails
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Step 5: Build the machine profile object
|
|
90
|
+
const machineProfile = {
|
|
91
|
+
hostname: hostname,
|
|
92
|
+
os: {
|
|
93
|
+
type: platform.type,
|
|
94
|
+
name: osName,
|
|
95
|
+
version: osVersion,
|
|
96
|
+
kernel: osRelease
|
|
97
|
+
},
|
|
98
|
+
arch: arch,
|
|
99
|
+
cpu: {
|
|
100
|
+
count: cpuCount,
|
|
101
|
+
model: os.cpus()[0] ? os.cpus()[0].model : 'unknown'
|
|
102
|
+
},
|
|
103
|
+
memory: {
|
|
104
|
+
total: totalMemory,
|
|
105
|
+
totalHuman: formatBytes(totalMemory)
|
|
106
|
+
},
|
|
107
|
+
packageManagers: packageManagers,
|
|
108
|
+
detectedAt: new Date().toISOString()
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// Step 6: Write the profile to disk (idempotent — overwrites if exists)
|
|
112
|
+
const MACHINES_DIR = path.join(os.homedir(), '.devutils', 'machines');
|
|
113
|
+
const CURRENT_FILE = path.join(MACHINES_DIR, 'current.json');
|
|
114
|
+
|
|
115
|
+
fs.mkdirSync(MACHINES_DIR, { recursive: true });
|
|
116
|
+
fs.writeFileSync(CURRENT_FILE, JSON.stringify(machineProfile, null, 2) + '\n');
|
|
117
|
+
|
|
118
|
+
// Step 7: Output the result
|
|
119
|
+
context.output.out(machineProfile);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
module.exports = { meta, run };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Machine service registration.
|
|
3
|
+
* Machine profiles and detection.
|
|
4
|
+
*/
|
|
5
|
+
module.exports = {
|
|
6
|
+
name: 'machine',
|
|
7
|
+
description: 'Machine profiles and detection',
|
|
8
|
+
commands: {
|
|
9
|
+
detect: () => require('./detect'),
|
|
10
|
+
show: () => require('./show'),
|
|
11
|
+
set: () => require('./set'),
|
|
12
|
+
list: () => require('./list'),
|
|
13
|
+
}
|
|
14
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
|
|
7
|
+
const meta = {
|
|
8
|
+
description: 'List all known machine profiles.',
|
|
9
|
+
arguments: [],
|
|
10
|
+
flags: []
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Lists all machine profiles stored in ~/.devutils/machines/.
|
|
15
|
+
* Reads every .json file in the directory and builds a summary for each,
|
|
16
|
+
* including hostname, OS, architecture, and detection timestamp.
|
|
17
|
+
* The current machine (current.json) is marked in the output.
|
|
18
|
+
*
|
|
19
|
+
* Right now this will only show the local machine. When backup/sync is
|
|
20
|
+
* built, profiles from other computers will also appear here.
|
|
21
|
+
*
|
|
22
|
+
* @param {object} args - Parsed command arguments (none expected).
|
|
23
|
+
* @param {object} context - The CLI context object with output and errors.
|
|
24
|
+
*/
|
|
25
|
+
async function run(args, context) {
|
|
26
|
+
const MACHINES_DIR = path.join(os.homedir(), '.devutils', 'machines');
|
|
27
|
+
|
|
28
|
+
// Check if the machines directory exists
|
|
29
|
+
if (!fs.existsSync(MACHINES_DIR)) {
|
|
30
|
+
context.errors.throwError(404, 'No machine profiles found. Run "dev machine detect" first.', 'machine');
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Find all JSON files in the machines directory
|
|
35
|
+
const files = fs.readdirSync(MACHINES_DIR).filter(f => f.endsWith('.json'));
|
|
36
|
+
|
|
37
|
+
if (files.length === 0) {
|
|
38
|
+
context.errors.throwError(404, 'No machine profiles found. Run "dev machine detect" first.', 'machine');
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Load each profile and build a summary
|
|
43
|
+
const machines = [];
|
|
44
|
+
|
|
45
|
+
for (const file of files) {
|
|
46
|
+
try {
|
|
47
|
+
const raw = fs.readFileSync(path.join(MACHINES_DIR, file), 'utf8');
|
|
48
|
+
const profile = JSON.parse(raw);
|
|
49
|
+
|
|
50
|
+
machines.push({
|
|
51
|
+
file: file,
|
|
52
|
+
current: file === 'current.json',
|
|
53
|
+
hostname: profile.hostname || 'unknown',
|
|
54
|
+
os: profile.os ? `${profile.os.name} ${profile.os.version}` : 'unknown',
|
|
55
|
+
arch: profile.arch || 'unknown',
|
|
56
|
+
detectedAt: profile.detectedAt || 'unknown'
|
|
57
|
+
});
|
|
58
|
+
} catch (err) {
|
|
59
|
+
// Don't crash on corrupted files — show them as error entries
|
|
60
|
+
machines.push({
|
|
61
|
+
file: file,
|
|
62
|
+
current: file === 'current.json',
|
|
63
|
+
hostname: 'error',
|
|
64
|
+
os: `Failed to read: ${err.message}`,
|
|
65
|
+
arch: 'unknown',
|
|
66
|
+
detectedAt: 'unknown'
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
context.output.out(machines);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
module.exports = { meta, run };
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
|
|
7
|
+
const meta = {
|
|
8
|
+
description: 'Set a value in the current machine profile.',
|
|
9
|
+
arguments: [
|
|
10
|
+
{ name: 'key', required: true, description: 'Dot-notation path to the value (e.g., nickname, os.version)' },
|
|
11
|
+
{ name: 'value', required: false, description: 'The value to set. Omit if using --json.' }
|
|
12
|
+
],
|
|
13
|
+
flags: [
|
|
14
|
+
{ name: 'json', type: 'string', description: 'Set a structured value using a JSON string' }
|
|
15
|
+
]
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Updates a single value in the current machine profile by dot-notation key.
|
|
20
|
+
* Reads ~/.devutils/machines/current.json, sets the value at the given path,
|
|
21
|
+
* and writes the file back. Preserves all other existing fields.
|
|
22
|
+
*
|
|
23
|
+
* Supports type coercion for simple values: "true"/"false" become booleans,
|
|
24
|
+
* "null" becomes null, numeric strings become numbers. Use --json for
|
|
25
|
+
* structured values like arrays or objects.
|
|
26
|
+
*
|
|
27
|
+
* @param {object} args - Parsed command arguments with positional[0]=key, positional[1]=value.
|
|
28
|
+
* @param {object} context - The CLI context object with output and errors.
|
|
29
|
+
*/
|
|
30
|
+
async function run(args, context) {
|
|
31
|
+
const key = args.positional[0];
|
|
32
|
+
const rawValue = args.positional[1];
|
|
33
|
+
const jsonValue = args.flags.json;
|
|
34
|
+
|
|
35
|
+
// Validate required arguments
|
|
36
|
+
if (!key) {
|
|
37
|
+
context.errors.throwError(400, 'Missing required argument: <key>. Example: dev machine set nickname "my-laptop"', 'machine');
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!rawValue && rawValue !== '' && !jsonValue) {
|
|
42
|
+
context.errors.throwError(400, 'Missing value. Provide a value or use --json for structured data.', 'machine');
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (rawValue && jsonValue) {
|
|
47
|
+
context.errors.throwError(400, 'Provide either a positional value or --json, not both.', 'machine');
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Parse the value with type coercion
|
|
52
|
+
let value;
|
|
53
|
+
|
|
54
|
+
if (jsonValue) {
|
|
55
|
+
try {
|
|
56
|
+
value = JSON.parse(jsonValue);
|
|
57
|
+
} catch (err) {
|
|
58
|
+
context.errors.throwError(400, `Invalid JSON: ${err.message}`, 'machine');
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
} else {
|
|
62
|
+
if (rawValue === 'true') {
|
|
63
|
+
value = true;
|
|
64
|
+
} else if (rawValue === 'false') {
|
|
65
|
+
value = false;
|
|
66
|
+
} else if (rawValue === 'null') {
|
|
67
|
+
value = null;
|
|
68
|
+
} else if (!isNaN(rawValue) && rawValue.trim() !== '') {
|
|
69
|
+
value = Number(rawValue);
|
|
70
|
+
} else {
|
|
71
|
+
value = rawValue;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Read the existing machine profile
|
|
76
|
+
const CURRENT_FILE = path.join(os.homedir(), '.devutils', 'machines', 'current.json');
|
|
77
|
+
|
|
78
|
+
if (!fs.existsSync(CURRENT_FILE)) {
|
|
79
|
+
context.errors.throwError(404, 'No machine profile found. Run "dev machine detect" first.', 'machine');
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const raw = fs.readFileSync(CURRENT_FILE, 'utf8');
|
|
84
|
+
const profile = JSON.parse(raw);
|
|
85
|
+
|
|
86
|
+
// Walk the dot-notation path and set the value
|
|
87
|
+
const parts = key.split('.');
|
|
88
|
+
let target = profile;
|
|
89
|
+
|
|
90
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
91
|
+
const part = parts[i];
|
|
92
|
+
if (!(part in target) || typeof target[part] !== 'object' || target[part] === null) {
|
|
93
|
+
target[part] = {};
|
|
94
|
+
}
|
|
95
|
+
target = target[part];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const lastPart = parts[parts.length - 1];
|
|
99
|
+
target[lastPart] = value;
|
|
100
|
+
|
|
101
|
+
// Write the updated profile back to disk
|
|
102
|
+
fs.writeFileSync(CURRENT_FILE, JSON.stringify(profile, null, 2) + '\n');
|
|
103
|
+
context.output.out({ key: key, value: value });
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
module.exports = { meta, run };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
|
|
7
|
+
const meta = {
|
|
8
|
+
description: 'Display the current machine profile.',
|
|
9
|
+
arguments: [],
|
|
10
|
+
flags: []
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Reads the current machine profile from ~/.devutils/machines/current.json
|
|
15
|
+
* and displays it. If the file does not exist, tells the user to run
|
|
16
|
+
* "dev machine detect" first.
|
|
17
|
+
*
|
|
18
|
+
* @param {object} args - Parsed command arguments (none expected).
|
|
19
|
+
* @param {object} context - The CLI context object with output and errors.
|
|
20
|
+
*/
|
|
21
|
+
async function run(args, context) {
|
|
22
|
+
const CURRENT_FILE = path.join(os.homedir(), '.devutils', 'machines', 'current.json');
|
|
23
|
+
|
|
24
|
+
if (!fs.existsSync(CURRENT_FILE)) {
|
|
25
|
+
context.errors.throwError(404, 'No machine profile found. Run "dev machine detect" first.', 'machine');
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const raw = fs.readFileSync(CURRENT_FILE, 'utf8');
|
|
30
|
+
const profile = JSON.parse(raw);
|
|
31
|
+
|
|
32
|
+
context.output.out(profile);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
module.exports = { meta, run };
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const meta = {
|
|
4
|
+
description: 'Introspect a command by dot-notation path. Returns description, arguments, and flags.',
|
|
5
|
+
arguments: [
|
|
6
|
+
{ name: 'path', required: false, description: 'Dot-notation path (e.g., config.set, api.gmail.messages.list)' },
|
|
7
|
+
],
|
|
8
|
+
flags: [],
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
async function run(args, context) {
|
|
12
|
+
const schema = require('../lib/schema');
|
|
13
|
+
const dotPath = args.positional[0];
|
|
14
|
+
|
|
15
|
+
if (!dotPath) {
|
|
16
|
+
// No path given: show top-level listing
|
|
17
|
+
const registry = schema.getRegistry();
|
|
18
|
+
const entries = [];
|
|
19
|
+
|
|
20
|
+
for (const [name, entry] of Object.entries(registry)) {
|
|
21
|
+
entries.push({
|
|
22
|
+
Name: name,
|
|
23
|
+
Type: entry.type || 'unknown',
|
|
24
|
+
Description: entry.description || '',
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (entries.length === 0) {
|
|
29
|
+
context.output.info('No commands found in registry.');
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
context.output.out(entries);
|
|
34
|
+
context.output.info(`\nUse "dev schema <path>" to inspect a specific command.`);
|
|
35
|
+
context.output.info('Example: dev schema config.set');
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const result = schema.resolve(dotPath);
|
|
40
|
+
|
|
41
|
+
if (!result) {
|
|
42
|
+
// Try to find suggestions by resolving the parent path
|
|
43
|
+
const parts = dotPath.split('.');
|
|
44
|
+
if (parts.length > 1) {
|
|
45
|
+
const parentPath = parts.slice(0, -1).join('.');
|
|
46
|
+
const parent = schema.resolve(parentPath);
|
|
47
|
+
if (parent) {
|
|
48
|
+
const children = parent.commands
|
|
49
|
+
? Object.keys(parent.commands)
|
|
50
|
+
: parent.resources
|
|
51
|
+
? Object.keys(parent.resources)
|
|
52
|
+
: [];
|
|
53
|
+
if (children.length > 0) {
|
|
54
|
+
context.output.error(`No command found at "${dotPath}".`);
|
|
55
|
+
context.output.info(`Available under "${parentPath}": ${children.join(', ')}`);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
context.output.error(`No command found at "${dotPath}".`);
|
|
62
|
+
context.output.info('Run "dev schema" to see all available paths.');
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Display the result
|
|
67
|
+
if (result.type === 'service') {
|
|
68
|
+
const cmds = Object.keys(result.commands || {});
|
|
69
|
+
if (context.flags.format === 'json') {
|
|
70
|
+
context.output.out(result);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
context.output.info(`Service: ${result.name}`);
|
|
74
|
+
context.output.info(`Description: ${result.description}`);
|
|
75
|
+
context.output.info('');
|
|
76
|
+
context.output.info(`Commands (${cmds.length}):`);
|
|
77
|
+
for (const cmdName of cmds) {
|
|
78
|
+
const cmd = result.commands[cmdName];
|
|
79
|
+
context.output.info(` ${cmdName.padEnd(20)} ${cmd.description || ''}`);
|
|
80
|
+
}
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (result.type === 'plugin') {
|
|
85
|
+
const resources = Object.keys(result.resources || {});
|
|
86
|
+
if (context.flags.format === 'json') {
|
|
87
|
+
context.output.out(result);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
context.output.info(`Plugin: ${result.name} (v${result.version})`);
|
|
91
|
+
context.output.info(`Description: ${result.description}`);
|
|
92
|
+
context.output.info(`Auth: ${result.auth || 'none'}`);
|
|
93
|
+
context.output.info('');
|
|
94
|
+
context.output.info(`Resources (${resources.length}):`);
|
|
95
|
+
for (const resName of resources) {
|
|
96
|
+
const res = result.resources[resName];
|
|
97
|
+
context.output.info(` ${resName.padEnd(20)} ${res.description || ''}`);
|
|
98
|
+
}
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (result.type === 'resource') {
|
|
103
|
+
const cmds = Object.keys(result.commands || {});
|
|
104
|
+
if (context.flags.format === 'json') {
|
|
105
|
+
context.output.out(result);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
context.output.info(`Resource: ${result.name}`);
|
|
109
|
+
context.output.info(`Description: ${result.description}`);
|
|
110
|
+
context.output.info('');
|
|
111
|
+
context.output.info(`Commands (${cmds.length}):`);
|
|
112
|
+
for (const cmdName of cmds) {
|
|
113
|
+
const cmd = result.commands[cmdName];
|
|
114
|
+
context.output.info(` ${cmdName.padEnd(20)} ${cmd.description || ''}`);
|
|
115
|
+
}
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (result.type === 'command') {
|
|
120
|
+
if (context.flags.format === 'json') {
|
|
121
|
+
context.output.out(result);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
context.output.info(`Command: ${result.name}`);
|
|
125
|
+
context.output.info(`Description: ${result.description}`);
|
|
126
|
+
|
|
127
|
+
const cmdArgs = result.arguments || [];
|
|
128
|
+
if (cmdArgs.length > 0) {
|
|
129
|
+
context.output.info('');
|
|
130
|
+
context.output.info('Arguments:');
|
|
131
|
+
for (const arg of cmdArgs) {
|
|
132
|
+
const req = arg.required ? '(required)' : '(optional)';
|
|
133
|
+
context.output.info(` ${(arg.name || '').padEnd(20)} ${req} ${arg.description || ''}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const cmdFlags = result.flags || [];
|
|
138
|
+
if (cmdFlags.length > 0) {
|
|
139
|
+
context.output.info('');
|
|
140
|
+
context.output.info('Flags:');
|
|
141
|
+
for (const flag of cmdFlags) {
|
|
142
|
+
context.output.info(` --${(flag.name || '').padEnd(18)} ${flag.description || ''}`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Fallback: just output the result
|
|
149
|
+
context.output.out(result);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
module.exports = { meta, run };
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const shell = require('../../lib/shell');
|
|
6
|
+
const { checkQmd } = require('./qmd');
|
|
7
|
+
|
|
8
|
+
const meta = {
|
|
9
|
+
description: 'Manage searchable collections (add, remove, list)',
|
|
10
|
+
arguments: [
|
|
11
|
+
{ name: 'action', description: 'Sub-command: add, remove, or list', required: true },
|
|
12
|
+
{ name: 'target', description: 'Directory path (for add) or collection name (for remove)', required: false }
|
|
13
|
+
],
|
|
14
|
+
flags: [
|
|
15
|
+
{ name: 'name', type: 'string', description: 'Collection name (required for add)' },
|
|
16
|
+
{ name: 'mask', type: 'string', description: 'Glob pattern to filter indexed files (e.g., "**/*.md")' },
|
|
17
|
+
{ name: 'confirm', type: 'boolean', description: 'Skip confirmation prompt (for remove)' }
|
|
18
|
+
]
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Manages QMD search collections: add, remove, or list.
|
|
23
|
+
* Each action shells out to the corresponding qmd collections command.
|
|
24
|
+
*
|
|
25
|
+
* @param {object} args - Parsed CLI arguments { positional, flags }.
|
|
26
|
+
* @param {object} context - CLI context { output, errors, prompt }.
|
|
27
|
+
*/
|
|
28
|
+
async function run(args, context) {
|
|
29
|
+
// Check for QMD availability first
|
|
30
|
+
const qmd = checkQmd();
|
|
31
|
+
if (!qmd.available) {
|
|
32
|
+
context.errors.throwError(1, qmd.message, 'search');
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const action = args.positional[0];
|
|
37
|
+
|
|
38
|
+
if (!action) {
|
|
39
|
+
context.output.info('Usage: dev search collections <add|remove|list>');
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (action === 'list') {
|
|
44
|
+
// List all registered collections
|
|
45
|
+
const result = await shell.exec('qmd collections list');
|
|
46
|
+
|
|
47
|
+
if (result.exitCode !== 0) {
|
|
48
|
+
context.errors.throwError(1, result.stderr || 'Failed to list collections.', 'search');
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Try to parse as JSON for structured output
|
|
53
|
+
const output = result.stdout;
|
|
54
|
+
try {
|
|
55
|
+
const parsed = JSON.parse(output);
|
|
56
|
+
context.output.out(parsed);
|
|
57
|
+
} catch (err) {
|
|
58
|
+
// Pass through as-is
|
|
59
|
+
context.output.info(output || 'No collections registered.');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
} else if (action === 'add') {
|
|
63
|
+
// Add a new collection
|
|
64
|
+
const target = args.positional[1];
|
|
65
|
+
|
|
66
|
+
if (!target) {
|
|
67
|
+
context.errors.throwError(400, 'Missing directory path. Example: dev search collections add /path/to/dir --name docs', 'search');
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const name = args.flags.name;
|
|
72
|
+
if (!name) {
|
|
73
|
+
context.errors.throwError(400, 'The --name flag is required when adding a collection.', 'search');
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Resolve to an absolute path
|
|
78
|
+
const dirPath = path.resolve(target);
|
|
79
|
+
if (!fs.existsSync(dirPath)) {
|
|
80
|
+
context.errors.throwError(1, `Directory does not exist: ${dirPath}`, 'search');
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Build the qmd command
|
|
85
|
+
let cmd = `qmd collections add "${dirPath}" --name "${name}"`;
|
|
86
|
+
if (args.flags.mask) {
|
|
87
|
+
cmd += ` --mask "${args.flags.mask}"`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const result = await shell.exec(cmd);
|
|
91
|
+
|
|
92
|
+
if (result.exitCode !== 0) {
|
|
93
|
+
context.errors.throwError(1, result.stderr || 'Failed to add collection.', 'search');
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
context.output.info(`Collection "${name}" added: ${dirPath}`);
|
|
98
|
+
|
|
99
|
+
} else if (action === 'remove') {
|
|
100
|
+
// Remove a collection
|
|
101
|
+
const name = args.positional[1];
|
|
102
|
+
|
|
103
|
+
if (!name) {
|
|
104
|
+
context.errors.throwError(400, 'Provide the collection name to remove. Example: dev search collections remove docs', 'search');
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Ask for confirmation unless --confirm is passed
|
|
109
|
+
if (!args.flags.confirm) {
|
|
110
|
+
const ok = await context.prompt.confirm(
|
|
111
|
+
`Remove collection "${name}"?`,
|
|
112
|
+
false
|
|
113
|
+
);
|
|
114
|
+
if (!ok) {
|
|
115
|
+
context.output.info('Cancelled.');
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const result = await shell.exec(`qmd collections remove "${name}"`);
|
|
121
|
+
|
|
122
|
+
if (result.exitCode !== 0) {
|
|
123
|
+
context.errors.throwError(1, result.stderr || `Failed to remove collection "${name}".`, 'search');
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
context.output.info(`Collection "${name}" removed.`);
|
|
128
|
+
|
|
129
|
+
} else {
|
|
130
|
+
context.output.info('Usage: dev search collections <add|remove|list>');
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
module.exports = { meta, run };
|