align repl with cli

This commit is contained in:
Robin Olsen
2026-03-11 14:05:57 +01:00
parent b5cbacd532
commit 2c8ba4e393
4 changed files with 87 additions and 14 deletions

View File

@@ -27,4 +27,5 @@ var (
TitleStyle = lipgloss.NewStyle().Foreground(PrimaryColor).Bold(true) TitleStyle = lipgloss.NewStyle().Foreground(PrimaryColor).Bold(true)
TextStyle = lipgloss.NewStyle().Foreground(TextColor) TextStyle = lipgloss.NewStyle().Foreground(TextColor)
HelpStyle = lipgloss.NewStyle().Align(lipgloss.Center).Foreground(AccentColor) HelpStyle = lipgloss.NewStyle().Align(lipgloss.Center).Foreground(AccentColor)
ErrorStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("1")).Bold(true) // Red color for errors
) )

View File

@@ -21,14 +21,70 @@ func execute(input string) (string, error) {
return ReplTitle, nil return ReplTitle, nil
} }
if input == "status" {
return executePMCommand("survey status")
}
if after, ok := strings.CutPrefix(input, "pm"); ok { if after, ok := strings.CutPrefix(input, "pm"); ok {
return executePMCommand(after) return executePMCommand(after)
} }
return executeShellCommand(input) return executeShellCommand(input)
} }
// shellSplit splits input respecting quoted strings and escapes
// similar to how a shell would parse arguments
func shellSplit(input string) []string {
var args []string
var current strings.Builder
var inQuote rune
var escaped bool
for _, ch := range input {
if escaped {
current.WriteRune(ch)
escaped = false
continue
}
if ch == '\\' {
escaped = true
continue
}
if inQuote != 0 {
if ch == inQuote {
inQuote = 0
continue
}
current.WriteRune(ch)
continue
}
if ch == '"' || ch == '\'' {
inQuote = ch
continue
}
if ch == ' ' || ch == '\t' {
if current.Len() > 0 {
args = append(args, current.String())
current.Reset()
}
continue
}
current.WriteRune(ch)
}
if current.Len() > 0 {
args = append(args, current.String())
}
return args
}
func executeShellCommand(input string) (string, error) { func executeShellCommand(input string) (string, error) {
parts := strings.Fields(input) parts := shellSplit(input)
if len(parts) == 0 { if len(parts) == 0 {
return "", nil return "", nil
} }
@@ -39,7 +95,7 @@ func executeShellCommand(input string) (string, error) {
} }
func executePMCommand(input string) (string, error) { func executePMCommand(input string) (string, error) {
parts := strings.Fields(input) parts := shellSplit(input)
if len(parts) == 0 { if len(parts) == 0 {
return "", nil return "", nil
} }

View File

@@ -22,8 +22,8 @@ type ValidationFeedback = models.ValidationFeedback
const ( const (
ReplHelp = `Type 'pm help' for available PM commands. ReplHelp = `Type 'pm help' for available PM commands.
Type 'pm status' to check task progress. You can also run shell commands directly. Type 'exit' or 'quit' to leave.
You can also run shell commands directly. Type 'exit' or 'quit' to leave.` Type 'status' to check task progress.`
ReplTitle = "Welcome to Project Management CLI! " + ReplHelp ReplTitle = "Welcome to Project Management CLI! " + ReplHelp
) )
@@ -144,8 +144,19 @@ func (r *REPL) Run(ctx context.Context, config app.Config) error {
// Add the input to the history for future navigation. // Add the input to the history for future navigation.
history = append(history, input) history = append(history, input)
output, _ := execute(input) // Ignore errors for now, gives better ux output, err := execute(input)
fmt.Println(style.TextStyle.Render(output)) // Print the output of the command in a styled format. if err != nil {
// Show command output (even on error) in normal text style
if output != "" {
fmt.Println(style.TextStyle.Render(output))
}
// Show error message in red if no output was captured
if output == "" {
fmt.Println(style.ErrorStyle.Render(err.Error()))
}
} else if output != "" {
fmt.Println(style.TextStyle.Render(output))
}
} }
return nil return nil

View File

@@ -12,6 +12,7 @@ import (
// rootSuggestions is a list of prompt suggestions for root-level commands. // rootSuggestions is a list of prompt suggestions for root-level commands.
var rootSuggestions = []prompt.Suggest{ var rootSuggestions = []prompt.Suggest{
{Text: "pm", Description: "Project Management System"}, {Text: "pm", Description: "Project Management System"},
{Text: "status", Description: "Show task status"},
{Text: "exit", Description: "Exit pm CLI"}, {Text: "exit", Description: "Exit pm CLI"},
{Text: "help", Description: "Show help information"}, {Text: "help", Description: "Show help information"},
{Text: "title", Description: "Print the welcome title"}, {Text: "title", Description: "Print the welcome title"},
@@ -38,7 +39,8 @@ var createFlags = []prompt.Suggest{
{Text: "--desc", Description: "Issue description"}, {Text: "--desc", Description: "Issue description"},
{Text: "--status", Description: "Issue status (open, closed, in_progress)"}, {Text: "--status", Description: "Issue status (open, closed, in_progress)"},
{Text: "--type", Description: "Issue type (bug, feature, task)"}, {Text: "--type", Description: "Issue type (bug, feature, task)"},
{Text: "--priority", Description: "Issue priority (0-5)"}, {Text: "--priority", Description: "Issue priority (0-4)"},
{Text: "--assignee", Description: "Issue assignee"},
} }
// updateFlags is a list of prompt suggestions for the update command flags. // updateFlags is a list of prompt suggestions for the update command flags.
@@ -47,7 +49,8 @@ var updateFlags = []prompt.Suggest{
{Text: "--desc", Description: "New issue description"}, {Text: "--desc", Description: "New issue description"},
{Text: "--status", Description: "New issue status (open, closed, in_progress)"}, {Text: "--status", Description: "New issue status (open, closed, in_progress)"},
{Text: "--type", Description: "New issue type (bug, feature, task)"}, {Text: "--type", Description: "New issue type (bug, feature, task)"},
{Text: "--priority", Description: "New issue priority (0-5)"}, {Text: "--priority", Description: "New issue priority (0-4)"},
{Text: "--assignee", Description: "New issue assignee"},
} }
// listFlags is a list of prompt suggestions for the list command flags. // listFlags is a list of prompt suggestions for the list command flags.
@@ -56,8 +59,9 @@ var listFlags = []prompt.Suggest{
{Text: "--desc", Description: "Filter by description"}, {Text: "--desc", Description: "Filter by description"},
{Text: "--status", Description: "Filter by status (open, closed, in_progress)"}, {Text: "--status", Description: "Filter by status (open, closed, in_progress)"},
{Text: "--type", Description: "Filter by type (bug, feature, task)"}, {Text: "--type", Description: "Filter by type (bug, feature, task)"},
{Text: "--priority", Description: "Filter by priority (0-5)"}, {Text: "--priority", Description: "Filter by priority (0-4)"},
{Text: "--limit", Description: "Limit number of results"}, {Text: "--limit", Description: "Limit number of results"},
{Text: "--assignee", Description: "Filter by assignee"},
} }
var deleteFlags = []prompt.Suggest{ var deleteFlags = []prompt.Suggest{
@@ -81,18 +85,19 @@ var statusValues = []prompt.Suggest{
// typeValues is a list of prompt suggestions for issue types // typeValues is a list of prompt suggestions for issue types
var typeValues = []prompt.Suggest{ var typeValues = []prompt.Suggest{
{Text: "task", Description: "Task issue type"},
{Text: "bug", Description: "Bug issue type"}, {Text: "bug", Description: "Bug issue type"},
{Text: "feature", Description: "Feature issue type"}, {Text: "feature", Description: "Feature issue type"},
{Text: "task", Description: "Task issue type"}, {Text: "chore", Description: "Chore issue type"},
} }
// priorityValues is a list of prompt suggestions for issue priority levels // priorityValues is a list of prompt suggestions for issue priority levels
var priorityValues = []prompt.Suggest{ var priorityValues = []prompt.Suggest{
{Text: "0", Description: "Lowest priority"}, {Text: "0", Description: "Irrelevant"},
{Text: "1", Description: "Low priority"}, {Text: "1", Description: "Low priority"},
{Text: "2", Description: "Medium-low priority"}, {Text: "2", Description: "Normal priority"},
{Text: "3", Description: "Medium priority"}, {Text: "3", Description: "High priority"},
{Text: "4", Description: "High priority"}, {Text: "4", Description: "Critical priority"},
} }
// isIDCommand maps command names to a boolean indicating whether they expect an issue ID as an argument. // isIDCommand maps command names to a boolean indicating whether they expect an issue ID as an argument.