make the repl experience better
This commit is contained in:
12
go.mod
12
go.mod
@@ -8,7 +8,9 @@ require (
|
||||
github.com/a-h/templ v0.3.977
|
||||
github.com/c-bata/go-prompt v0.2.6
|
||||
github.com/charmbracelet/bubbletea v1.3.10
|
||||
github.com/charmbracelet/fang v0.4.4
|
||||
github.com/charmbracelet/huh v0.8.0
|
||||
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/muesli/reflow v0.3.0
|
||||
github.com/rs/cors v1.11.1
|
||||
@@ -18,6 +20,7 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
charm.land/lipgloss/v2 v2.0.0-beta.3.0.20251106193318-19329a3e8410 // indirect
|
||||
git.sr.ht/~sbinet/gg v0.7.0 // indirect
|
||||
github.com/a-h/parse v0.0.0-20250122154542-74294addb73e // indirect
|
||||
github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b // indirect
|
||||
@@ -31,12 +34,15 @@ require (
|
||||
github.com/charmbracelet/bubbles v0.21.1 // indirect
|
||||
github.com/charmbracelet/colorprofile v0.4.1 // indirect
|
||||
github.com/charmbracelet/glamour v0.10.0 // indirect
|
||||
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834 // indirect
|
||||
github.com/charmbracelet/ultraviolet v0.0.0-20251106190538-99ea45596692 // indirect
|
||||
github.com/charmbracelet/x/ansi v0.11.5 // indirect
|
||||
github.com/charmbracelet/x/cellbuf v0.0.15 // indirect
|
||||
github.com/charmbracelet/x/exp/charmtone v0.0.0-20250603201427-c31516f43444 // indirect
|
||||
github.com/charmbracelet/x/exp/slice v0.0.0-20260203065841-a1c614051099 // indirect
|
||||
github.com/charmbracelet/x/exp/strings v0.1.0 // indirect
|
||||
github.com/charmbracelet/x/term v0.2.2 // indirect
|
||||
github.com/charmbracelet/x/termios v0.1.1 // indirect
|
||||
github.com/charmbracelet/x/windows v0.2.2 // indirect
|
||||
github.com/cli/browser v1.3.0 // indirect
|
||||
github.com/clipperhouse/displaywidth v0.9.0 // indirect
|
||||
github.com/clipperhouse/stringish v0.1.1 // indirect
|
||||
@@ -62,6 +68,10 @@ require (
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
|
||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||
github.com/muesli/mango v0.1.0 // indirect
|
||||
github.com/muesli/mango-cobra v1.2.0 // indirect
|
||||
github.com/muesli/mango-pflag v0.1.0 // indirect
|
||||
github.com/muesli/roff v0.1.0 // indirect
|
||||
github.com/muesli/termenv v0.16.0 // indirect
|
||||
github.com/natefinch/atomic v1.0.1 // indirect
|
||||
github.com/ncruces/go-sqlite3 v0.30.5 // indirect
|
||||
|
||||
22
go.sum
22
go.sum
@@ -1,3 +1,5 @@
|
||||
charm.land/lipgloss/v2 v2.0.0-beta.3.0.20251106193318-19329a3e8410 h1:D9PbaszZYpB4nj+d6HTWr1onlmlyuGVNfL9gAi8iB3k=
|
||||
charm.land/lipgloss/v2 v2.0.0-beta.3.0.20251106193318-19329a3e8410/go.mod h1:1qZyvvVCenJO2M1ac2mX0yyiIZJoZmDM4DG4s0udJkU=
|
||||
git.sr.ht/~sbinet/cmpimg v0.1.0 h1:E0zPRk2muWuCqSKSVZIWsgtU9pjsw3eKHi8VmQeScxo=
|
||||
git.sr.ht/~sbinet/cmpimg v0.1.0/go.mod h1:FU12psLbF4TfNXkKH2ZZQ29crIqoiqTZmeQ7dkp/pxE=
|
||||
git.sr.ht/~sbinet/gg v0.7.0 h1:YmNf7YKd7diDMTPm86hZa1EM3pbkOyD/zzjl0LZUdNM=
|
||||
@@ -45,12 +47,16 @@ github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlv
|
||||
github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
|
||||
github.com/charmbracelet/colorprofile v0.4.1 h1:a1lO03qTrSIRaK8c3JRxJDZOvhvIeSco3ej+ngLk1kk=
|
||||
github.com/charmbracelet/colorprofile v0.4.1/go.mod h1:U1d9Dljmdf9DLegaJ0nGZNJvoXAhayhmidOdcBwAvKk=
|
||||
github.com/charmbracelet/fang v0.4.4 h1:G4qKxF6or/eTPgmAolwPuRNyuci3hTUGGX1rj1YkHJY=
|
||||
github.com/charmbracelet/fang v0.4.4/go.mod h1:P5/DNb9DddQ0Z0dbc0P3ol4/ix5Po7Ofr2KMBfAqoCo=
|
||||
github.com/charmbracelet/glamour v0.10.0 h1:MtZvfwsYCx8jEPFJm3rIBFIMZUfUJ765oX8V6kXldcY=
|
||||
github.com/charmbracelet/glamour v0.10.0/go.mod h1:f+uf+I/ChNmqo087elLnVdCiVgjSKWuXa/l6NU2ndYk=
|
||||
github.com/charmbracelet/huh v0.8.0 h1:Xz/Pm2h64cXQZn/Jvele4J3r7DDiqFCNIVteYukxDvY=
|
||||
github.com/charmbracelet/huh v0.8.0/go.mod h1:5YVc+SlZ1IhQALxRPpkGwwEKftN/+OlJlnJYlDRFqN4=
|
||||
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834 h1:ZR7e0ro+SZZiIZD7msJyA+NjkCNNavuiPBLgerbOziE=
|
||||
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834/go.mod h1:aKC/t2arECF6rNOnaKaVU6y4t4ZeHQzqfxedE/VkVhA=
|
||||
github.com/charmbracelet/ultraviolet v0.0.0-20251106190538-99ea45596692 h1:r/3jQZ1LjWW6ybp8HHfhrKrwHIWiJhUuY7wwYIWZulQ=
|
||||
github.com/charmbracelet/ultraviolet v0.0.0-20251106190538-99ea45596692/go.mod h1:Y8B4DzWeTb0ama8l3+KyopZtkE8fZjwRQ3aEAPEXHE0=
|
||||
github.com/charmbracelet/x/ansi v0.11.5 h1:NBWeBpj/lJPE3Q5l+Lusa4+mH6v7487OP8K0r1IhRg4=
|
||||
github.com/charmbracelet/x/ansi v0.11.5/go.mod h1:2JNYLgQUsyqaiLovhU2Rv/pb8r6ydXKS3NIttu3VGZQ=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.15 h1:ur3pZy0o6z/R7EylET877CBxaiE1Sp1GMxoFPAIztPI=
|
||||
@@ -59,8 +65,10 @@ github.com/charmbracelet/x/conpty v0.1.0 h1:4zc8KaIcbiL4mghEON8D72agYtSeIgq8FSTh
|
||||
github.com/charmbracelet/x/conpty v0.1.0/go.mod h1:rMFsDJoDwVmiYM10aD4bH2XiRgwI7NYJtQgl5yskjEQ=
|
||||
github.com/charmbracelet/x/errors v0.0.0-20240508181413-e8d8b6e2de86 h1:JSt3B+U9iqk37QUU2Rvb6DSBYRLtWqFqfxf8l5hOZUA=
|
||||
github.com/charmbracelet/x/errors v0.0.0-20240508181413-e8d8b6e2de86/go.mod h1:2P0UgXMEa6TsToMSuFqKFQR+fZTO9CNGUNokkPatT/0=
|
||||
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 h1:payRxjMjKgx2PaCWLZ4p3ro9y97+TVLZNaRZgJwSVDQ=
|
||||
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
|
||||
github.com/charmbracelet/x/exp/charmtone v0.0.0-20250603201427-c31516f43444 h1:IJDiTgVE56gkAGfq0lBEloWgkXMk4hl/bmuPoicI4R0=
|
||||
github.com/charmbracelet/x/exp/charmtone v0.0.0-20250603201427-c31516f43444/go.mod h1:T9jr8CzFpjhFVHjNjKwbAD7KwBNyFnj2pntAO7F2zw0=
|
||||
github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f h1:pk6gmGpCE7F3FcjaOEKYriCvpmIN4+6OS/RD0vm4uIA=
|
||||
github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f/go.mod h1:IfZAMTHB6XkZSeXUqriemErjAWCCzT0LwjKFYCZyw0I=
|
||||
github.com/charmbracelet/x/exp/slice v0.0.0-20260203065841-a1c614051099 h1:H09krOypYvXsPG4kYl+9J3tCRhST8WI3WEpXJELw+eg=
|
||||
github.com/charmbracelet/x/exp/slice v0.0.0-20260203065841-a1c614051099/go.mod h1:vqEfX6xzqW1pKKZUUiFOKg0OQ7bCh54Q2vR/tserrRA=
|
||||
github.com/charmbracelet/x/exp/strings v0.1.0 h1:i69S2XI7uG1u4NLGeJPSYU++Nmjvpo9nwd6aoEm7gkA=
|
||||
@@ -69,6 +77,8 @@ github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSg
|
||||
github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
|
||||
github.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8JawjaNZY=
|
||||
github.com/charmbracelet/x/termios v0.1.1/go.mod h1:rB7fnv1TgOPOyyKRJ9o+AsTU/vK5WHJ2ivHeut/Pcwo=
|
||||
github.com/charmbracelet/x/windows v0.2.2 h1:IofanmuvaxnKHuV04sC0eBy/smG6kIKrWG2/jYn2GuM=
|
||||
github.com/charmbracelet/x/windows v0.2.2/go.mod h1:/8XtdKZzedat74NQFn0NGlGL4soHB0YQZrETF96h75k=
|
||||
github.com/charmbracelet/x/xpty v0.1.2 h1:Pqmu4TEJ8KeA9uSkISKMU3f+C1F6OGBn8ABuGlqCbtI=
|
||||
github.com/charmbracelet/x/xpty v0.1.2/go.mod h1:XK2Z0id5rtLWcpeNiMYBccNNBrP2IJnzHI0Lq13Xzq4=
|
||||
github.com/cli/browser v1.3.0 h1:LejqCrpWr+1pRqmEPDGnTZOjsMe7sehifLynZJuqJpo=
|
||||
@@ -155,8 +165,16 @@ github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
|
||||
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
||||
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||
github.com/muesli/mango v0.1.0 h1:DZQK45d2gGbql1arsYA4vfg4d7I9Hfx5rX/GCmzsAvI=
|
||||
github.com/muesli/mango v0.1.0/go.mod h1:5XFpbC8jY5UUv89YQciiXNlbi+iJgt29VDC5xbzrLL4=
|
||||
github.com/muesli/mango-cobra v1.2.0 h1:DQvjzAM0PMZr85Iv9LIMaYISpTOliMEg+uMFtNbYvWg=
|
||||
github.com/muesli/mango-cobra v1.2.0/go.mod h1:vMJL54QytZAJhCT13LPVDfkvCUJ5/4jNUKF/8NC2UjA=
|
||||
github.com/muesli/mango-pflag v0.1.0 h1:UADqbYgpUyRoBja3g6LUL+3LErjpsOwaC9ywvBWe7Sg=
|
||||
github.com/muesli/mango-pflag v0.1.0/go.mod h1:YEQomTxaCUp8PrbhFh10UfbhbQrM/xJ4i2PB8VTLLW0=
|
||||
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
|
||||
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
|
||||
github.com/muesli/roff v0.1.0 h1:YD0lalCotmYuF5HhZliKWlIx7IEhiXeSfq7hNjFqGF8=
|
||||
github.com/muesli/roff v0.1.0/go.mod h1:pjAHQM9hdUUwm/krAfrLGgJkXJ+YuhtsfZ42kieB2Ig=
|
||||
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
|
||||
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
|
||||
github.com/natefinch/atomic v1.0.1 h1:ZPYKxkqQOx3KZ+RsbnP/YsgvxWQPGxjC0oBt2AhwV0A=
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/LazyBachelor/LazyPM/pkg"
|
||||
"github.com/LazyBachelor/LazyPM/pkg/cli/commands"
|
||||
"github.com/c-bata/go-prompt"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/muesli/reflow/truncate"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
@@ -29,13 +30,22 @@ func RunREPL(ctx context.Context, config pkg.SurveyConfig) error {
|
||||
|
||||
commands.SetServices(svc)
|
||||
|
||||
fmt.Printf("Welcome to Project Management CLI! Type 'pm help' for available commands.\n")
|
||||
fmt.Printf("You can also run shell commands directly. Type 'exit' or 'quit' to leave.\n")
|
||||
titleStyle := lipgloss.NewStyle().Align(lipgloss.Center).Bold(true).Border(lipgloss.RoundedBorder()).Padding(1).Foreground(lipgloss.Color("6"))
|
||||
|
||||
title := `Welcome to Project Management CLI! Type 'pm help' for available commands.
|
||||
You can also run shell commands directly. Type 'exit' or 'quit' to leave.`
|
||||
|
||||
fmt.Print("")
|
||||
fmt.Println(titleStyle.Render(title))
|
||||
|
||||
// Create persistent history
|
||||
var history []string
|
||||
|
||||
for {
|
||||
input := prompt.Input(
|
||||
"› ",
|
||||
completer,
|
||||
prompt.OptionPrefixTextColor(prompt.Cyan),
|
||||
prompt.OptionMaxSuggestion(5),
|
||||
prompt.OptionSuggestionBGColor(prompt.DefaultColor),
|
||||
prompt.OptionSelectedSuggestionBGColor(prompt.DefaultColor),
|
||||
@@ -43,6 +53,7 @@ func RunREPL(ctx context.Context, config pkg.SurveyConfig) error {
|
||||
prompt.OptionSelectedDescriptionBGColor(prompt.DefaultColor),
|
||||
prompt.OptionPreviewSuggestionBGColor(prompt.DefaultColor),
|
||||
prompt.OptionScrollbarBGColor(prompt.DefaultColor),
|
||||
prompt.OptionHistory(history),
|
||||
)
|
||||
|
||||
input = strings.TrimSpace(input)
|
||||
@@ -56,6 +67,19 @@ func RunREPL(ctx context.Context, config pkg.SurveyConfig) error {
|
||||
break
|
||||
}
|
||||
|
||||
if input == "help" {
|
||||
fmt.Println("Type 'pm help' for available PM commands.\nYou can also run shell commands directly. Type 'exit' or 'quit' to leave.")
|
||||
continue
|
||||
}
|
||||
|
||||
if input == "title" {
|
||||
fmt.Println(titleStyle.Render(title))
|
||||
continue
|
||||
}
|
||||
|
||||
// Add to history
|
||||
history = append(history, input)
|
||||
|
||||
// Check if it's a PM command (starts with "pm ")
|
||||
if after, ok := strings.CutPrefix(input, "pm "); ok {
|
||||
// Strip "pm " prefix and execute as PM command
|
||||
@@ -72,14 +96,26 @@ func RunREPL(ctx context.Context, config pkg.SurveyConfig) error {
|
||||
}
|
||||
} else {
|
||||
// Execute as shell command
|
||||
// Save terminal state before shell command
|
||||
state, _ := term.GetState(int(os.Stdin.Fd()))
|
||||
|
||||
cmd := exec.Command("sh", "-c", input)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Stdin = os.Stdin
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
// Restore terminal state even on error
|
||||
if state != nil {
|
||||
term.Restore(int(os.Stdin.Fd()), state)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Restore terminal state for go-prompt
|
||||
if state != nil {
|
||||
term.Restore(int(os.Stdin.Fd()), state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,11 +131,33 @@ func completer(d prompt.Document) []prompt.Suggest {
|
||||
text := d.TextBeforeCursor()
|
||||
words := strings.Fields(text)
|
||||
|
||||
// Only provide PM command completions if input starts with "pm"
|
||||
if len(words) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if words[0] != "pm" {
|
||||
suggestions := []prompt.Suggest{
|
||||
{Text: "pm", Description: "Project Management System"},
|
||||
{Text: "exit", Description: "Exit pm CLI"},
|
||||
{Text: "help", Description: "Show help information"},
|
||||
{Text: "title", Description: "Print the welcome title"},
|
||||
{Text: "git", Description: "Version control system"},
|
||||
}
|
||||
|
||||
if len(words) == 0 {
|
||||
return suggestions
|
||||
}
|
||||
|
||||
var filtered []prompt.Suggest
|
||||
for _, s := range suggestions {
|
||||
if strings.HasPrefix(s.Text, words[0]) {
|
||||
filtered = append(filtered, s)
|
||||
}
|
||||
}
|
||||
|
||||
return filtered
|
||||
}
|
||||
|
||||
// If first word is "pm", provide PM command completions
|
||||
if words[0] == "pm" {
|
||||
// Remove "pm" from words to get the actual command
|
||||
@@ -120,14 +178,10 @@ func completer(d prompt.Document) []prompt.Suggest {
|
||||
func commandSuggestions(words []string) []prompt.Suggest {
|
||||
suggestions := []prompt.Suggest{
|
||||
{Text: "help", Description: "Show help information"},
|
||||
{Text: "create", Description: "Create a new issue"},
|
||||
{Text: "delete", Description: "Delete an issue by ID"},
|
||||
{Text: "create", Description: "Create a new issue with title"},
|
||||
{Text: "describe", Description: "Get issue details by ID"},
|
||||
{Text: "list", Description: "List all issues"},
|
||||
{Text: "ls", Description: "List all issues"},
|
||||
{Text: "search", Description: "Alias for ls"},
|
||||
{Text: "get", Description: "Alias for describe"},
|
||||
{Text: "read", Description: "Alias for describe"},
|
||||
{Text: "add", Description: "Alias for create"},
|
||||
}
|
||||
|
||||
if len(words) == 0 {
|
||||
@@ -174,6 +228,9 @@ func flagSuggestions(cmd string, words []string, text string) []prompt.Suggest {
|
||||
|
||||
case "describe", "get", "read":
|
||||
return issueIdSuggestions(words)
|
||||
|
||||
case "delete":
|
||||
return issueIdSuggestions(words)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user