Skip to content

Commit e75030e

Browse files
committed
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.
1 parent 74b25b4 commit e75030e

9 files changed

Lines changed: 997 additions & 1 deletion

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: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ require (
2121
cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect
2222
cloud.google.com/go/compute/metadata v0.3.0 // indirect
2323
cloud.google.com/go/longrunning v0.5.7 // indirect
24+
github.com/briandowns/spinner v1.23.2 // indirect
25+
github.com/fatih/color v1.7.0 // indirect
2426
github.com/felixge/httpsnoop v1.0.4 // indirect
2527
github.com/go-logr/logr v1.4.1 // indirect
2628
github.com/go-logr/stdr v1.2.2 // indirect
@@ -34,6 +36,7 @@ require (
3436
github.com/mattn/go-colorable v0.1.2 // indirect
3537
github.com/mattn/go-isatty v0.0.8 // indirect
3638
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
39+
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
3740
github.com/spf13/pflag v1.0.5 // indirect
3841
go.opencensus.io v0.24.0 // indirect
3942
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.51.0 // 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 := f.WriteString(fmt.Sprintf("\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)