@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,143 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
|
|
7
|
+
const DEVUTILS_DIR = path.join(os.homedir(), '.devutils');
|
|
8
|
+
const CONFIG_FILE = path.join(DEVUTILS_DIR, 'config.json');
|
|
9
|
+
|
|
10
|
+
const meta = {
|
|
11
|
+
description: 'First-run onboarding wizard. Sets up ~/.devutils/ and creates config.json.',
|
|
12
|
+
arguments: [],
|
|
13
|
+
flags: [
|
|
14
|
+
{ name: 'force', type: 'boolean', description: 'Re-run setup even if already configured' },
|
|
15
|
+
{ name: 'profile', type: 'string', description: 'Set the profile name (skip the prompt)' },
|
|
16
|
+
],
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Detects the user's shell config file path for PATH modification.
|
|
21
|
+
* @returns {{ shell: string, file: string }|null}
|
|
22
|
+
*/
|
|
23
|
+
function getShellConfig() {
|
|
24
|
+
const shell = process.env.SHELL || '';
|
|
25
|
+
const home = os.homedir();
|
|
26
|
+
|
|
27
|
+
if (shell.includes('zsh')) {
|
|
28
|
+
return { shell: 'zsh', file: path.join(home, '.zshrc') };
|
|
29
|
+
}
|
|
30
|
+
if (shell.includes('bash')) {
|
|
31
|
+
return { shell: 'bash', file: path.join(home, '.bashrc') };
|
|
32
|
+
}
|
|
33
|
+
if (shell.includes('fish')) {
|
|
34
|
+
return { shell: 'fish', file: path.join(home, '.config', 'fish', 'config.fish') };
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Adds the devutils bin directory to the user's PATH via shell config.
|
|
41
|
+
* Idempotent: checks if the line is already present before adding.
|
|
42
|
+
* @param {string} shellFile - The shell config file path.
|
|
43
|
+
*/
|
|
44
|
+
function addToPathFile(shellFile) {
|
|
45
|
+
const exportLine = 'export PATH="$HOME/.devutils/bin:$PATH"';
|
|
46
|
+
|
|
47
|
+
// Create the file if it doesn't exist
|
|
48
|
+
if (!fs.existsSync(shellFile)) {
|
|
49
|
+
fs.mkdirSync(path.dirname(shellFile), { recursive: true });
|
|
50
|
+
fs.writeFileSync(shellFile, '', 'utf8');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const content = fs.readFileSync(shellFile, 'utf8');
|
|
54
|
+
|
|
55
|
+
// Check if the line is already there
|
|
56
|
+
if (content.includes('.devutils/bin')) {
|
|
57
|
+
return false; // Already present
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
fs.appendFileSync(shellFile, '\n# DevUtils CLI\n' + exportLine + '\n');
|
|
61
|
+
return true; // Added
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function run(args, context) {
|
|
65
|
+
// Check if already configured
|
|
66
|
+
if (fs.existsSync(CONFIG_FILE) && !args.flags.force) {
|
|
67
|
+
context.output.info('DevUtils is already configured. Use --force to re-run setup.');
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Create the directory structure
|
|
72
|
+
fs.mkdirSync(path.join(DEVUTILS_DIR, 'machines'), { recursive: true });
|
|
73
|
+
fs.mkdirSync(path.join(DEVUTILS_DIR, 'bin'), { recursive: true });
|
|
74
|
+
fs.mkdirSync(path.join(DEVUTILS_DIR, 'auth'), { recursive: true });
|
|
75
|
+
fs.mkdirSync(path.join(DEVUTILS_DIR, 'plugins'), { recursive: true });
|
|
76
|
+
fs.mkdirSync(path.join(DEVUTILS_DIR, 'utils'), { recursive: true });
|
|
77
|
+
fs.mkdirSync(path.join(DEVUTILS_DIR, 'cache'), { recursive: true });
|
|
78
|
+
|
|
79
|
+
// Prompt for user info
|
|
80
|
+
const name = await context.prompt.ask('Your full name', '');
|
|
81
|
+
const email = await context.prompt.ask('Your email address', '');
|
|
82
|
+
const url = await context.prompt.ask('Your URL (optional)', '');
|
|
83
|
+
|
|
84
|
+
// Prompt for backup backend
|
|
85
|
+
const backupBackend = await context.prompt.choose(
|
|
86
|
+
'Where should DevUtils store configuration backups?',
|
|
87
|
+
['repo', 'gist'],
|
|
88
|
+
0
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
// Prompt for profile name
|
|
92
|
+
const profile = args.flags.profile || await context.prompt.ask('Profile name for this machine', 'default');
|
|
93
|
+
|
|
94
|
+
// Build and write config
|
|
95
|
+
const config = {
|
|
96
|
+
user: {
|
|
97
|
+
name: name,
|
|
98
|
+
email: email,
|
|
99
|
+
url: url,
|
|
100
|
+
},
|
|
101
|
+
defaults: {
|
|
102
|
+
license: 'MIT',
|
|
103
|
+
packageManager: 'npm',
|
|
104
|
+
},
|
|
105
|
+
backup: {
|
|
106
|
+
backend: backupBackend,
|
|
107
|
+
location: null,
|
|
108
|
+
},
|
|
109
|
+
profile: profile,
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2) + '\n');
|
|
113
|
+
|
|
114
|
+
// Offer to add ~/.devutils/bin to PATH
|
|
115
|
+
const shellConfig = getShellConfig();
|
|
116
|
+
if (shellConfig) {
|
|
117
|
+
const addToPath = await context.prompt.confirm(
|
|
118
|
+
'Add ~/.devutils/bin to your PATH? (required for aliases to work)',
|
|
119
|
+
true
|
|
120
|
+
);
|
|
121
|
+
if (addToPath) {
|
|
122
|
+
const added = addToPathFile(shellConfig.file);
|
|
123
|
+
if (added) {
|
|
124
|
+
context.output.info(`Added to ${shellConfig.file}. Restart your terminal or run: source ${shellConfig.file}`);
|
|
125
|
+
} else {
|
|
126
|
+
context.output.info('PATH entry already exists. No changes made.');
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Print summary
|
|
132
|
+
context.output.info('');
|
|
133
|
+
context.output.info('DevUtils configured successfully!');
|
|
134
|
+
context.output.info('');
|
|
135
|
+
context.output.info(` Name: ${name || '(not set)'}`);
|
|
136
|
+
context.output.info(` Email: ${email || '(not set)'}`);
|
|
137
|
+
context.output.info(` Profile: ${profile}`);
|
|
138
|
+
context.output.info(` Backup: ${backupBackend}`);
|
|
139
|
+
context.output.info(` Config: ${CONFIG_FILE}`);
|
|
140
|
+
context.output.info('');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
module.exports = { meta, run };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
|
|
7
|
+
const CONFIG_FILE = path.join(os.homedir(), '.devutils', 'config.json');
|
|
8
|
+
|
|
9
|
+
const meta = {
|
|
10
|
+
description: 'Reset configuration to defaults. Clears user info and restores default settings.',
|
|
11
|
+
arguments: [],
|
|
12
|
+
flags: [
|
|
13
|
+
{ name: 'confirm', type: 'boolean', description: 'Skip the confirmation prompt' },
|
|
14
|
+
],
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
async function run(args, context) {
|
|
18
|
+
if (!fs.existsSync(CONFIG_FILE)) {
|
|
19
|
+
context.errors.throwError(404, 'Config not found. Nothing to reset.', 'config');
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Ask for confirmation unless --confirm is passed
|
|
24
|
+
if (!args.flags.confirm) {
|
|
25
|
+
const proceed = await context.prompt.confirm(
|
|
26
|
+
'This will reset all configuration to defaults. Your user info will be cleared. Continue?',
|
|
27
|
+
false
|
|
28
|
+
);
|
|
29
|
+
if (!proceed) {
|
|
30
|
+
context.output.info('Reset cancelled.');
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Write default config
|
|
36
|
+
const defaults = {
|
|
37
|
+
user: {
|
|
38
|
+
name: '',
|
|
39
|
+
email: '',
|
|
40
|
+
url: '',
|
|
41
|
+
},
|
|
42
|
+
defaults: {
|
|
43
|
+
license: 'MIT',
|
|
44
|
+
packageManager: 'npm',
|
|
45
|
+
},
|
|
46
|
+
backup: {
|
|
47
|
+
backend: null,
|
|
48
|
+
location: null,
|
|
49
|
+
},
|
|
50
|
+
profile: 'default',
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(defaults, null, 2) + '\n');
|
|
54
|
+
context.output.info('Configuration reset to defaults.');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = { meta, run };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
|
|
7
|
+
const CONFIG_FILE = path.join(os.homedir(), '.devutils', 'config.json');
|
|
8
|
+
|
|
9
|
+
const meta = {
|
|
10
|
+
description: 'Write a config value by dot-notation key.',
|
|
11
|
+
arguments: [
|
|
12
|
+
{ name: 'key', required: true, description: 'Dot-notation path to the config value (e.g., user.email)' },
|
|
13
|
+
{ name: 'value', required: false, description: 'The value to set. Omit if using --json.' },
|
|
14
|
+
],
|
|
15
|
+
flags: [
|
|
16
|
+
{ name: 'json', type: 'string', description: 'Set a structured value using a JSON string' },
|
|
17
|
+
],
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
async function run(args, context) {
|
|
21
|
+
const key = args.positional[0];
|
|
22
|
+
const rawValue = args.positional[1];
|
|
23
|
+
const jsonValue = args.flags.json;
|
|
24
|
+
|
|
25
|
+
if (!key) {
|
|
26
|
+
context.errors.throwError(400, 'Missing required argument: <key>. Example: dev config set user.email fred@example.com', 'config');
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!rawValue && rawValue !== '' && !jsonValue) {
|
|
31
|
+
context.errors.throwError(400, 'Missing value. Provide a value or use --json for structured data.', 'config');
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (rawValue && jsonValue) {
|
|
36
|
+
context.errors.throwError(400, 'Provide either a positional value or --json, not both.', 'config');
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Parse the value
|
|
41
|
+
let value;
|
|
42
|
+
if (jsonValue) {
|
|
43
|
+
try {
|
|
44
|
+
value = JSON.parse(jsonValue);
|
|
45
|
+
} catch (err) {
|
|
46
|
+
context.errors.throwError(400, `Invalid JSON: ${err.message}`, 'config');
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
} else {
|
|
50
|
+
// Coerce simple types
|
|
51
|
+
if (rawValue === 'true') {
|
|
52
|
+
value = true;
|
|
53
|
+
} else if (rawValue === 'false') {
|
|
54
|
+
value = false;
|
|
55
|
+
} else if (rawValue === 'null') {
|
|
56
|
+
value = null;
|
|
57
|
+
} else if (!isNaN(rawValue) && rawValue.trim() !== '') {
|
|
58
|
+
value = Number(rawValue);
|
|
59
|
+
} else {
|
|
60
|
+
value = rawValue;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Read existing config
|
|
65
|
+
if (!fs.existsSync(CONFIG_FILE)) {
|
|
66
|
+
context.errors.throwError(404, 'Config not found. Run "dev config init" first.', 'config');
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const raw = fs.readFileSync(CONFIG_FILE, 'utf8');
|
|
71
|
+
const config = JSON.parse(raw);
|
|
72
|
+
|
|
73
|
+
// Set value by dot-notation key
|
|
74
|
+
const parts = key.split('.');
|
|
75
|
+
let target = config;
|
|
76
|
+
|
|
77
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
78
|
+
const part = parts[i];
|
|
79
|
+
if (!(part in target) || typeof target[part] !== 'object' || target[part] === null) {
|
|
80
|
+
target[part] = {};
|
|
81
|
+
}
|
|
82
|
+
target = target[part];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const lastPart = parts[parts.length - 1];
|
|
86
|
+
target[lastPart] = value;
|
|
87
|
+
|
|
88
|
+
// Write config back
|
|
89
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2) + '\n');
|
|
90
|
+
context.output.out({ key: key, value: value });
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
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 CONFIG_FILE = path.join(os.homedir(), '.devutils', 'config.json');
|
|
8
|
+
|
|
9
|
+
const meta = {
|
|
10
|
+
description: 'Display the current configuration.',
|
|
11
|
+
arguments: [],
|
|
12
|
+
flags: [
|
|
13
|
+
{ name: 'profile', type: 'string', description: 'Show a specific profile instead of the active one' },
|
|
14
|
+
],
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
async function run(args, context) {
|
|
18
|
+
if (!fs.existsSync(CONFIG_FILE)) {
|
|
19
|
+
context.errors.throwError(404, 'Config not found. Run "dev config init" first.', 'config');
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const raw = fs.readFileSync(CONFIG_FILE, 'utf8');
|
|
24
|
+
const config = JSON.parse(raw);
|
|
25
|
+
|
|
26
|
+
// Handle --profile flag
|
|
27
|
+
if (args.flags.profile && args.flags.profile !== config.profile) {
|
|
28
|
+
context.errors.throwError(404, `Profile "${args.flags.profile}" not found. Current profile is "${config.profile}".`, 'config');
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
context.output.out(config);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
module.exports = { meta, run };
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* help command.
|
|
5
|
+
* Lists all services and top-level commands, or shows detailed help
|
|
6
|
+
* for a specific service or command when given arguments.
|
|
7
|
+
*
|
|
8
|
+
* Examples:
|
|
9
|
+
* dev help - List all services and top-level commands
|
|
10
|
+
* dev help config - List all commands in the config service
|
|
11
|
+
* dev help config set - Show detailed help for config set
|
|
12
|
+
* dev help version - Show help for the version command
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const path = require('path');
|
|
16
|
+
|
|
17
|
+
const meta = {
|
|
18
|
+
description: 'Show usage information',
|
|
19
|
+
arguments: [
|
|
20
|
+
{ name: 'command', description: 'Command path to get help for (e.g., config set)', required: false, variadic: true }
|
|
21
|
+
],
|
|
22
|
+
flags: []
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Known service directory names. Each one has an index.js that exports
|
|
27
|
+
* name, description, and a commands map.
|
|
28
|
+
* @type {string[]}
|
|
29
|
+
*/
|
|
30
|
+
const SERVICE_NAMES = [
|
|
31
|
+
'config', 'machine', 'identity', 'tools', 'ignore',
|
|
32
|
+
'util', 'alias', 'auth', 'api', 'ai', 'search'
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Top-level commands (not inside a service folder). Each entry has a
|
|
37
|
+
* name and a description. Descriptions are read from the command's
|
|
38
|
+
* meta export when available, with fallbacks for stubs that haven't
|
|
39
|
+
* been filled in yet.
|
|
40
|
+
* @type {Array<{ name: string, fallback: string }>}
|
|
41
|
+
*/
|
|
42
|
+
const TOP_LEVEL_COMMANDS = [
|
|
43
|
+
{ name: 'status', fallback: 'Overall health check' },
|
|
44
|
+
{ name: 'update', fallback: 'Update DevUtils CLI' },
|
|
45
|
+
{ name: 'version', fallback: 'Show the current installed version' },
|
|
46
|
+
{ name: 'schema', fallback: 'Show or validate config schema' },
|
|
47
|
+
{ name: 'help', fallback: 'Show usage information' },
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Loads a service index.js and returns { name, description, commands }.
|
|
52
|
+
* Returns null if the service cannot be loaded.
|
|
53
|
+
*
|
|
54
|
+
* @param {string} serviceName - The service directory name (e.g., 'config').
|
|
55
|
+
* @returns {object|null}
|
|
56
|
+
*/
|
|
57
|
+
function loadService(serviceName) {
|
|
58
|
+
try {
|
|
59
|
+
return require(path.join(__dirname, serviceName, 'index'));
|
|
60
|
+
} catch {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Loads a top-level command module and returns it.
|
|
67
|
+
* Returns null if the module cannot be loaded.
|
|
68
|
+
*
|
|
69
|
+
* @param {string} commandName - The command file name (e.g., 'version').
|
|
70
|
+
* @returns {object|null}
|
|
71
|
+
*/
|
|
72
|
+
function loadTopLevelCommand(commandName) {
|
|
73
|
+
try {
|
|
74
|
+
return require(path.join(__dirname, commandName));
|
|
75
|
+
} catch {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Gets the description for a top-level command. Reads from the command's
|
|
82
|
+
* meta.description if available, otherwise uses the fallback.
|
|
83
|
+
*
|
|
84
|
+
* @param {{ name: string, fallback: string }} entry - The top-level command entry.
|
|
85
|
+
* @returns {string}
|
|
86
|
+
*/
|
|
87
|
+
function getTopLevelDescription(entry) {
|
|
88
|
+
const cmd = loadTopLevelCommand(entry.name);
|
|
89
|
+
if (cmd && cmd.meta && cmd.meta.description) {
|
|
90
|
+
return cmd.meta.description;
|
|
91
|
+
}
|
|
92
|
+
return entry.fallback;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Builds a list of all services with their names and descriptions.
|
|
97
|
+
*
|
|
98
|
+
* @returns {Array<{ name: string, description: string }>}
|
|
99
|
+
*/
|
|
100
|
+
function buildServiceList() {
|
|
101
|
+
const services = [];
|
|
102
|
+
for (const name of SERVICE_NAMES) {
|
|
103
|
+
const svc = loadService(name);
|
|
104
|
+
if (svc) {
|
|
105
|
+
services.push({ name: svc.name, description: svc.description });
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return services;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Builds a list of all top-level commands with their names and descriptions.
|
|
113
|
+
*
|
|
114
|
+
* @returns {Array<{ name: string, description: string }>}
|
|
115
|
+
*/
|
|
116
|
+
function buildTopLevelCommandList() {
|
|
117
|
+
return TOP_LEVEL_COMMANDS.map(entry => ({
|
|
118
|
+
name: entry.name,
|
|
119
|
+
description: getTopLevelDescription(entry),
|
|
120
|
+
}));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Pads a string to the given width with trailing spaces.
|
|
125
|
+
*
|
|
126
|
+
* @param {string} str - The string to pad.
|
|
127
|
+
* @param {number} width - The target width.
|
|
128
|
+
* @returns {string}
|
|
129
|
+
*/
|
|
130
|
+
function pad(str, width) {
|
|
131
|
+
return str + ' '.repeat(Math.max(0, width - str.length));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Shows the top-level help listing: all services and top-level commands.
|
|
136
|
+
*
|
|
137
|
+
* @param {object} context - The CLI context object.
|
|
138
|
+
*/
|
|
139
|
+
function showTopLevelHelp(context) {
|
|
140
|
+
const services = buildServiceList();
|
|
141
|
+
const commands = buildTopLevelCommandList();
|
|
142
|
+
|
|
143
|
+
if (context.flags.format === 'json') {
|
|
144
|
+
context.output.out({ services, commands });
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Find the longest name for alignment
|
|
149
|
+
const allNames = [...services.map(s => s.name), ...commands.map(c => c.name)];
|
|
150
|
+
const maxLen = Math.max(...allNames.map(n => n.length));
|
|
151
|
+
const colWidth = maxLen + 4;
|
|
152
|
+
|
|
153
|
+
context.output.info('Usage: dev <service> <command> [arguments] [flags]');
|
|
154
|
+
context.output.info('');
|
|
155
|
+
context.output.info('Services:');
|
|
156
|
+
for (const svc of services) {
|
|
157
|
+
context.output.info(` ${pad(svc.name, colWidth)}${svc.description}`);
|
|
158
|
+
}
|
|
159
|
+
context.output.info('');
|
|
160
|
+
context.output.info('Commands:');
|
|
161
|
+
for (const cmd of commands) {
|
|
162
|
+
context.output.info(` ${pad(cmd.name, colWidth)}${cmd.description}`);
|
|
163
|
+
}
|
|
164
|
+
context.output.info('');
|
|
165
|
+
context.output.info('Run "dev help <service>" to see commands within a service.');
|
|
166
|
+
context.output.info('Run "dev help <service> <command>" for detailed command help.');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Shows help for a specific service, listing all its commands.
|
|
171
|
+
*
|
|
172
|
+
* @param {object} service - The service module (with name, description, commands).
|
|
173
|
+
* @param {object} context - The CLI context object.
|
|
174
|
+
*/
|
|
175
|
+
function showServiceHelp(service, context) {
|
|
176
|
+
const commandNames = Object.keys(service.commands || {});
|
|
177
|
+
const commandList = [];
|
|
178
|
+
|
|
179
|
+
for (const name of commandNames) {
|
|
180
|
+
let description = '';
|
|
181
|
+
try {
|
|
182
|
+
const cmd = service.commands[name]();
|
|
183
|
+
if (cmd && cmd.meta && cmd.meta.description) {
|
|
184
|
+
description = cmd.meta.description;
|
|
185
|
+
}
|
|
186
|
+
} catch {
|
|
187
|
+
// Command module may not be fully implemented yet
|
|
188
|
+
}
|
|
189
|
+
commandList.push({ name, description });
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (context.flags.format === 'json') {
|
|
193
|
+
context.output.out({
|
|
194
|
+
service: service.name,
|
|
195
|
+
description: service.description,
|
|
196
|
+
commands: commandList,
|
|
197
|
+
});
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const maxLen = Math.max(...commandList.map(c => c.name.length));
|
|
202
|
+
const colWidth = maxLen + 4;
|
|
203
|
+
|
|
204
|
+
context.output.info(`${service.name}: ${service.description}`);
|
|
205
|
+
context.output.info('');
|
|
206
|
+
context.output.info('Commands:');
|
|
207
|
+
for (const cmd of commandList) {
|
|
208
|
+
const desc = cmd.description ? `${pad(cmd.name, colWidth)}${cmd.description}` : cmd.name;
|
|
209
|
+
context.output.info(` dev ${service.name} ${desc}`);
|
|
210
|
+
}
|
|
211
|
+
context.output.info('');
|
|
212
|
+
context.output.info(`Run "dev help ${service.name} <command>" for detailed help.`);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Shows detailed help for a specific command, including its description,
|
|
217
|
+
* arguments, and flags.
|
|
218
|
+
*
|
|
219
|
+
* @param {object} command - The command module (with meta.description, meta.arguments, meta.flags).
|
|
220
|
+
* @param {string} commandPath - The full command path for display (e.g., "config set").
|
|
221
|
+
* @param {object} context - The CLI context object.
|
|
222
|
+
*/
|
|
223
|
+
function showCommandHelp(command, commandPath, context) {
|
|
224
|
+
const m = command.meta || {};
|
|
225
|
+
const args = m.arguments || [];
|
|
226
|
+
const flags = m.flags || [];
|
|
227
|
+
|
|
228
|
+
if (context.flags.format === 'json') {
|
|
229
|
+
context.output.out({
|
|
230
|
+
command: commandPath,
|
|
231
|
+
description: m.description || '',
|
|
232
|
+
arguments: args,
|
|
233
|
+
flags: flags,
|
|
234
|
+
});
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
context.output.info(`dev ${commandPath}`);
|
|
239
|
+
context.output.info('');
|
|
240
|
+
if (m.description) {
|
|
241
|
+
context.output.info(` ${m.description}`);
|
|
242
|
+
context.output.info('');
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (args.length > 0) {
|
|
246
|
+
context.output.info('Arguments:');
|
|
247
|
+
const maxLen = Math.max(...args.map(a => a.name.length));
|
|
248
|
+
const colWidth = maxLen + 4;
|
|
249
|
+
for (const arg of args) {
|
|
250
|
+
const req = arg.required ? '(required)' : '(optional)';
|
|
251
|
+
context.output.info(` ${pad(arg.name, colWidth)}${arg.description || ''} ${req}`);
|
|
252
|
+
}
|
|
253
|
+
context.output.info('');
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (flags.length > 0) {
|
|
257
|
+
context.output.info('Flags:');
|
|
258
|
+
const maxLen = Math.max(...flags.map(f => `--${f.name}`.length));
|
|
259
|
+
const colWidth = maxLen + 4;
|
|
260
|
+
for (const flag of flags) {
|
|
261
|
+
const name = `--${flag.name}`;
|
|
262
|
+
const parts = [flag.description || ''];
|
|
263
|
+
if (flag.type) {
|
|
264
|
+
parts.push(`(${flag.type})`);
|
|
265
|
+
}
|
|
266
|
+
if (flag.default !== undefined) {
|
|
267
|
+
parts.push(`[default: ${flag.default}]`);
|
|
268
|
+
}
|
|
269
|
+
context.output.info(` ${pad(name, colWidth)}${parts.join(' ')}`);
|
|
270
|
+
}
|
|
271
|
+
context.output.info('');
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Main entry point for the help command.
|
|
277
|
+
*
|
|
278
|
+
* @param {object} args - Parsed command arguments. Positional args are the command path.
|
|
279
|
+
* @param {object} context - The CLI context object.
|
|
280
|
+
*/
|
|
281
|
+
async function run(args, context) {
|
|
282
|
+
const positional = args.positional || [];
|
|
283
|
+
|
|
284
|
+
// No arguments: show top-level help
|
|
285
|
+
if (positional.length === 0) {
|
|
286
|
+
showTopLevelHelp(context);
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const firstName = positional[0];
|
|
291
|
+
const secondName = positional[1];
|
|
292
|
+
|
|
293
|
+
// Check if the first argument is a service name
|
|
294
|
+
const service = loadService(firstName);
|
|
295
|
+
if (service) {
|
|
296
|
+
if (!secondName) {
|
|
297
|
+
// Show service-level help (list all commands in the service)
|
|
298
|
+
showServiceHelp(service, context);
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Show help for a specific command within the service
|
|
303
|
+
if (service.commands && service.commands[secondName]) {
|
|
304
|
+
try {
|
|
305
|
+
const cmd = service.commands[secondName]();
|
|
306
|
+
showCommandHelp(cmd, `${firstName} ${secondName}`, context);
|
|
307
|
+
return;
|
|
308
|
+
} catch {
|
|
309
|
+
// Fall through to unknown command error
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Unknown command within the service
|
|
314
|
+
const methods = Object.keys(service.commands || {}).join(', ');
|
|
315
|
+
context.errors.throwError(
|
|
316
|
+
404,
|
|
317
|
+
`Unknown command "${secondName}" for service "${firstName}". Available commands: ${methods}`,
|
|
318
|
+
'help'
|
|
319
|
+
);
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Check if the first argument is a top-level command
|
|
324
|
+
const topCmd = loadTopLevelCommand(firstName);
|
|
325
|
+
if (topCmd) {
|
|
326
|
+
showCommandHelp(topCmd, firstName, context);
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Unknown command
|
|
331
|
+
context.errors.throwError(
|
|
332
|
+
404,
|
|
333
|
+
`Unknown command "${firstName}". Run "dev help" to see available commands.`,
|
|
334
|
+
'help'
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
module.exports = { meta, run };
|