Skip to content

Commit ed60edd

Browse files
authored
Wave 2 - More more more (#10)
* Add new utility commands and update documentation Introduces several new CLI commands: myip, port-check, jwt-decode, qr, regex, base, timer, loc, install-completion, and string utilities. Updates README with usage for these commands, registers them in the root command, and adds required dependencies. Implements command logic for network, developer tools, productivity, and string manipulation utilities. * Refactor string case handling and improve resource cleanup Replaced deprecated strings.Title with golang.org/x/text/cases.Title for title casing in string commands. Improved resource cleanup in network commands by wrapping Close calls in anonymous functions and ignoring errors. Minor refactor in completion command to use fmt.Fprintf for writing to files. Updated go.mod to move some dependencies from indirect to direct. * Bump CLI version to 1.2.0 Updated the Version variable in version.go to reflect the new release version 1.2.0.
1 parent 74b25b4 commit ed60edd

10 files changed

Lines changed: 1001 additions & 3 deletions

File tree

README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,16 @@ mayrlabs git prune-stale
329329
| `mayrlabs version` | Display the version of mayrlabs CLI |
330330
| `mayrlabs visit` | Open the MayR Labs website in your browser |
331331
| `mayrlabs github` | Open the mayrlabs-go GitHub repository in your browser |
332-
332+
| `mayrlabs myip` | Show your public and local IP addresses |
333+
| `mayrlabs port-check [port]` | Check if a port is open or in use on localhost |
334+
| `mayrlabs jwt-decode [token]` | Decode a JWT token (Header and Payload) |
335+
| `mayrlabs qr [text]` | Generate a QR code (optional save to file) |
336+
| `mayrlabs regex [pattern] [text]` | Test a regex pattern against a string |
337+
| `mayrlabs base [from] [num] [to]` | Convert numbers between bases (2-36) |
338+
| `mayrlabs timer [duration]` | Start a countdown timer (e.g., 15s, 1.5m) |
339+
| `mayrlabs loc [path]` | Count lines of code grouped by language |
340+
| `mayrlabs install-completion` | Install shell autocompletion (zsh, bash, fish, powershell) |
341+
| `mayrlabs string` | String utilities (case, escape, reverse, mask, length, slugify) |
333342
---
334343

335344
### 🔧 System Commands

cmd/root.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,4 +84,16 @@ func init() {
8484
rootCmd.AddCommand(commands.FlutterCmd)
8585
rootCmd.AddCommand(commands.PHPCmd)
8686
rootCmd.AddCommand(commands.JSCmd)
87+
88+
// New commands for v1.2.0
89+
rootCmd.AddCommand(commands.MyIPCmd)
90+
rootCmd.AddCommand(commands.PortCheckCmd)
91+
rootCmd.AddCommand(commands.JWTDecodeCmd)
92+
rootCmd.AddCommand(commands.QRCmd)
93+
rootCmd.AddCommand(commands.RegexCmd)
94+
rootCmd.AddCommand(commands.BaseCmd)
95+
rootCmd.AddCommand(commands.StringCmd)
96+
rootCmd.AddCommand(commands.TimerCmd)
97+
rootCmd.AddCommand(commands.LOCCmd)
98+
rootCmd.AddCommand(commands.InstallCompletionCmd)
8799
}

go.mod

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@ toolchain go1.24.7
77
require (
88
github.com/AlecAivazis/survey/v2 v2.3.7
99
github.com/atotto/clipboard v0.1.4
10+
github.com/briandowns/spinner v1.23.2
1011
github.com/google/generative-ai-go v0.20.1
1112
github.com/google/uuid v1.6.0
1213
github.com/oklog/ulid/v2 v2.1.1
14+
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
1315
github.com/spf13/cobra v1.8.0
16+
golang.org/x/text v0.21.0
1417
google.golang.org/api v0.186.0
1518
)
1619

@@ -21,6 +24,7 @@ require (
2124
cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect
2225
cloud.google.com/go/compute/metadata v0.3.0 // indirect
2326
cloud.google.com/go/longrunning v0.5.7 // indirect
27+
github.com/fatih/color v1.7.0 // indirect
2428
github.com/felixge/httpsnoop v1.0.4 // indirect
2529
github.com/go-logr/logr v1.4.1 // indirect
2630
github.com/go-logr/stdr v1.2.2 // indirect
@@ -47,7 +51,6 @@ require (
4751
golang.org/x/sync v0.10.0 // indirect
4852
golang.org/x/sys v0.28.0 // indirect
4953
golang.org/x/term v0.27.0 // indirect
50-
golang.org/x/text v0.21.0 // indirect
5154
golang.org/x/time v0.5.0 // indirect
5255
google.golang.org/genproto/googleapis/api v0.0.0-20240617180043-68d350f18fd4 // indirect
5356
google.golang.org/genproto/googleapis/rpc v0.0.0-20240617180043-68d350f18fd4 // indirect

go.sum

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63n
1818
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
1919
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
2020
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
21+
github.com/briandowns/spinner v1.23.2 h1:Zc6ecUnI+YzLmJniCfDNaMbW0Wid1d5+qcTq4L2FW8w=
22+
github.com/briandowns/spinner v1.23.2/go.mod h1:LaZeM4wm2Ywy6vO571mvhQNRcWfRUnXOs0RcKV0wYKM=
2123
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
2224
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
2325
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
@@ -31,6 +33,8 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
3133
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
3234
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
3335
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
36+
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
37+
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
3438
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
3539
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
3640
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
@@ -92,6 +96,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
9296
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
9397
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
9498
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
99+
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
100+
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
95101
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
96102
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
97103
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=

internal/commands/completion.go

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package commands
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"path/filepath"
7+
"strings"
8+
9+
"github.com/MayR-Labs/mayrlabs-go/internal/utils"
10+
"github.com/spf13/cobra"
11+
)
12+
13+
// InstallCompletionCmd installs autocompletion for the specified shell
14+
var InstallCompletionCmd = &cobra.Command{
15+
Use: "install-completion [shell...]",
16+
Short: "Install autocompletion for your shell",
17+
Long: "Install autocompletion for zsh, bash, fish, or powershell. You can select multiple shells.",
18+
RunE: func(cmd *cobra.Command, args []string) error {
19+
shells := args
20+
if len(shells) == 0 {
21+
selected, err := utils.PromptMultiSelect(
22+
"Select shells to install completion for:",
23+
[]string{"zsh", "bash", "fish", "powershell"},
24+
)
25+
if err != nil {
26+
return err
27+
}
28+
shells = selected
29+
}
30+
31+
if len(shells) == 0 {
32+
return fmt.Errorf("no shells selected")
33+
}
34+
35+
homeDir, err := os.UserHomeDir()
36+
if err != nil {
37+
return fmt.Errorf("failed to get home directory: %w", err)
38+
}
39+
40+
configDir := filepath.Join(homeDir, ".mayrlabs")
41+
if err := os.MkdirAll(configDir, 0755); err != nil {
42+
return fmt.Errorf("failed to create config directory: %w", err)
43+
}
44+
45+
for _, shell := range shells {
46+
fmt.Printf("Installing completion for %s...\n", shell)
47+
48+
var completionFile string
49+
var rcFile string
50+
var sourceCmd string
51+
52+
switch shell {
53+
case "zsh":
54+
completionFile = filepath.Join(configDir, "completion.zsh")
55+
rcFile = filepath.Join(homeDir, ".zshrc")
56+
sourceCmd = fmt.Sprintf("source %s", completionFile)
57+
// Generate completion
58+
if err := cmd.Root().GenZshCompletionFile(completionFile); err != nil {
59+
fmt.Printf("❌ Failed to generate zsh completion: %v\n", err)
60+
continue
61+
}
62+
case "bash":
63+
completionFile = filepath.Join(configDir, "completion.bash")
64+
rcFile = filepath.Join(homeDir, ".bashrc")
65+
// On macOS, it might be .bash_profile
66+
if _, err := os.Stat(rcFile); os.IsNotExist(err) {
67+
profile := filepath.Join(homeDir, ".bash_profile")
68+
if _, err := os.Stat(profile); err == nil {
69+
rcFile = profile
70+
}
71+
}
72+
sourceCmd = fmt.Sprintf("source %s", completionFile)
73+
if err := cmd.Root().GenBashCompletionFile(completionFile); err != nil {
74+
fmt.Printf("❌ Failed to generate bash completion: %v\n", err)
75+
continue
76+
}
77+
case "fish":
78+
completionFile = filepath.Join(configDir, "completion.fish")
79+
configPath := filepath.Join(homeDir, ".config", "fish")
80+
rcFile = filepath.Join(configPath, "config.fish")
81+
sourceCmd = fmt.Sprintf("source %s", completionFile)
82+
83+
// Ensure fish config dir exists
84+
_ = os.MkdirAll(configPath, 0755)
85+
86+
if err := cmd.Root().GenFishCompletionFile(completionFile, true); err != nil {
87+
fmt.Printf("❌ Failed to generate fish completion: %v\n", err)
88+
continue
89+
}
90+
case "powershell":
91+
completionFile = filepath.Join(configDir, "completion.ps1")
92+
// PowerShell profile path is variable, but usually in Documents
93+
// This is a simplification
94+
documentsDir := filepath.Join(homeDir, "Documents")
95+
// Check for OneDrive
96+
oneDriveDocs := filepath.Join(homeDir, "OneDrive", "Documents")
97+
if _, err := os.Stat(oneDriveDocs); err == nil {
98+
documentsDir = oneDriveDocs
99+
}
100+
101+
psDir := filepath.Join(documentsDir, "PowerShell")
102+
_ = os.MkdirAll(psDir, 0755)
103+
rcFile = filepath.Join(psDir, "Microsoft.PowerShell_profile.ps1")
104+
105+
sourceCmd = fmt.Sprintf(". %s", completionFile)
106+
if err := cmd.Root().GenPowerShellCompletionFile(completionFile); err != nil {
107+
fmt.Printf("❌ Failed to generate powershell completion: %v\n", err)
108+
continue
109+
}
110+
default:
111+
fmt.Printf("⚠️ Unsupported shell: %s\n", shell)
112+
continue
113+
}
114+
115+
// Add to RC file if not present
116+
if rcFile != "" {
117+
// Check if RC file exists, if not create it
118+
f, err := os.OpenFile(rcFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
119+
if err != nil {
120+
fmt.Printf("⚠️ Could not open RC file %s: %v\n", rcFile, err)
121+
fmt.Printf(" Please manually add this line to your config: %s\n", sourceCmd)
122+
continue
123+
}
124+
125+
content, err := os.ReadFile(rcFile)
126+
if err == nil {
127+
if !strings.Contains(string(content), completionFile) {
128+
if _, err := fmt.Fprintf(f, "\n# mayrlabs completion\n%s\n", sourceCmd); err != nil {
129+
fmt.Printf("⚠️ Failed to write to RC file: %v\n", err)
130+
} else {
131+
fmt.Printf("✅ Added source command to %s\n", rcFile)
132+
}
133+
} else {
134+
fmt.Printf("ℹ️ Completion already configured in %s\n", rcFile)
135+
}
136+
}
137+
_ = f.Close()
138+
}
139+
140+
fmt.Printf("✅ Completion installed for %s!\n", shell)
141+
}
142+
143+
fmt.Println("\n🎉 Installation complete! Please restart your shell or source your config file to apply changes.")
144+
return nil
145+
},
146+
}

0 commit comments

Comments
 (0)