Updated interaction between custom CLI syntax and Cobra flags

This commit is contained in:
2026-02-19 18:31:30 +01:00
parent cd77443a07
commit acab4333a7
2 changed files with 98 additions and 27 deletions
+47 -12
View File
@@ -15,6 +15,7 @@ type ParsedArgs struct {
Command string
Filters []string
Modifiers []string
CmdArgIndex int // position of command in os.Args[1:], -1 if not found
}
// Context key for parsed args
@@ -84,28 +85,29 @@ func Execute() error {
if len(os.Args) > 1 {
firstArg := os.Args[1]
if firstArg == "-h" || firstArg == "--help" || firstArg == "help" {
// Let Cobra handle help - skip preprocessing
return rootCmd.Execute()
}
}
// Preprocess arguments BEFORE Cobra routing
// Preprocess arguments (read-only scan — os.Args is never mutated)
if len(os.Args) > 1 {
parsed := preprocessArgs(os.Args[1:])
// Store in context for commands to use
ctx := context.WithValue(context.Background(), parsedArgsKey, parsed)
rootCmd.SetContext(ctx)
// Rewrite os.Args for Cobra based on parsed command
// This allows Cobra to route to the correct command
if parsed.Command != "list" || len(parsed.Filters) > 0 || len(parsed.Modifiers) > 0 {
// Reconstruct args: [command, ...filters, ...modifiers]
newArgs := []string{os.Args[0], parsed.Command}
newArgs = append(newArgs, parsed.Filters...)
newArgs = append(newArgs, parsed.Modifiers...)
os.Args = newArgs
// Build clean args for Cobra via SetArgs (os.Args stays untouched).
if parsed.CmdArgIndex >= 0 {
i := parsed.CmdArgIndex + 1 // offset for binary name in os.Args
cmdAndAfter := os.Args[i:] // command + subcommands + their flags
preCmdFlags := collectFlags(os.Args[1:i]) // persistent flags before command
cobraArgs := make([]string, 0, len(cmdAndAfter)+len(preCmdFlags))
cobraArgs = append(cobraArgs, cmdAndAfter...)
cobraArgs = append(cobraArgs, preCmdFlags...)
rootCmd.SetArgs(cobraArgs)
}
// CmdArgIndex == -1: no command found, don't call SetArgs.
// Cobra processes os.Args naturally → root command → default report.
}
return rootCmd.Execute()
@@ -130,6 +132,7 @@ func preprocessArgs(args []string) *ParsedArgs {
Command: "list", // Default command
Filters: []string{},
Modifiers: []string{},
CmdArgIndex: -1,
}
}
@@ -170,6 +173,7 @@ func preprocessArgs(args []string) *ParsedArgs {
Command: "list",
Filters: stripFlags(args),
Modifiers: []string{},
CmdArgIndex: -1,
}
}
@@ -186,6 +190,7 @@ func preprocessArgs(args []string) *ParsedArgs {
Command: cmdName,
Filters: leftArgs,
Modifiers: rightArgs,
CmdArgIndex: cmdIdx,
}
} else {
allFilters := append(leftArgs, rightArgs...)
@@ -193,6 +198,7 @@ func preprocessArgs(args []string) *ParsedArgs {
Command: cmdName,
Filters: allFilters,
Modifiers: []string{},
CmdArgIndex: cmdIdx,
}
}
}
@@ -208,6 +214,35 @@ func stripFlags(args []string) []string {
return result
}
// collectFlags extracts flag arguments (with their values) from a slice.
// Uses Cobra's persistent flag registry to determine if a flag takes a value.
func collectFlags(args []string) []string {
var flags []string
for i := 0; i < len(args); i++ {
if !strings.HasPrefix(args[i], "-") {
continue
}
flags = append(flags, args[i])
// If flag uses = syntax, value is already included
if strings.Contains(args[i], "=") {
continue
}
// Check if this flag takes a value argument
if i+1 < len(args) {
name := strings.TrimLeft(args[i], "-")
f := rootCmd.PersistentFlags().Lookup(name)
if f == nil && len(name) == 1 {
f = rootCmd.PersistentFlags().ShorthandLookup(name)
}
if f != nil && f.Value.Type() != "bool" {
i++
flags = append(flags, args[i])
}
}
}
return flags
}
func init() {
// Add persistent flags for directory overrides
rootCmd.PersistentFlags().StringVar(&configDirFlag, "config-dir", "",
+36
View File
@@ -2,3 +2,39 @@
`Buy milk due:8d wait:5d` still showing up
# Missing uncomplete feat
Undo / uncomplete fails in web-ui. task still has checked box and strikethrough.
# Cycling priority - Web
Trying to edit a task priority results in following console error:
GQLaRcBw.js:1 PUT https://opal.jnss.me/api/tasks/8814798c-97af-4134-9786-47e027d164c8 400 (Bad Request)
window.fetch @ GQLaRcBw.js:1
n @ nlPNz7OE.js:1
update @ nlPNz7OE.js:1
updateTask @ 2.qiXA3Mmu.js:1
X @ 2.qiXA3Mmu.js:3
Q @ 2.qiXA3Mmu.js:1
(anonymous) @ idxpmzXF.js:1
Qe @ jWcw5lls.js:1
n @ idxpmzXF.js:1
nlPNz7OE.js:1 API Error [/tasks/8814798c-97af-4134-9786-47e027d164c8]: Error: HTTP 400:
at n (nlPNz7OE.js:1:1556)
at async Object.updateTask (2.qiXA3Mmu.js:1:4502)
at async X (2.qiXA3Mmu.js:3:3519)
at async HTMLDivElement.Q (2.qiXA3Mmu.js:1:58579)
n @ nlPNz7OE.js:1
await in n
update @ nlPNz7OE.js:1
updateTask @ 2.qiXA3Mmu.js:1
X @ 2.qiXA3Mmu.js:3
Q @ 2.qiXA3Mmu.js:1
(anonymous) @ idxpmzXF.js:1
Qe @ jWcw5lls.js:1
n @ idxpmzXF.js:1
2.qiXA3Mmu.js:3 Failed to update task: Error: HTTP 400:
at n (nlPNz7OE.js:1:1556)
at async Object.updateTask (2.qiXA3Mmu.js:1:4502)
at async X (2.qiXA3Mmu.js:3:3519)
at async HTMLDivElement.Q (2.qiXA3Mmu.js:1:58579)
# Ambiguity complete and details tap.
Pressing the task description completes the task. only the checkbox click should complete task, otherwise open details view. to complete swipe left