diff --git a/server/cmd/multica/cmd_agent.go b/server/cmd/multica/cmd_agent.go index 37c0d1a9..f62e6228 100644 --- a/server/cmd/multica/cmd_agent.go +++ b/server/cmd/multica/cmd_agent.go @@ -29,7 +29,7 @@ var agentListCmd = &cobra.Command{ var agentGetCmd = &cobra.Command{ Use: "get ", Short: "Get agent details", - Args: cobra.ExactArgs(1), + Args: exactArgs(1), RunE: runAgentGet, } @@ -42,28 +42,28 @@ var agentCreateCmd = &cobra.Command{ var agentUpdateCmd = &cobra.Command{ Use: "update ", Short: "Update an agent", - Args: cobra.ExactArgs(1), + Args: exactArgs(1), RunE: runAgentUpdate, } var agentArchiveCmd = &cobra.Command{ Use: "archive ", Short: "Archive an agent", - Args: cobra.ExactArgs(1), + Args: exactArgs(1), RunE: runAgentArchive, } var agentRestoreCmd = &cobra.Command{ Use: "restore ", Short: "Restore an archived agent", - Args: cobra.ExactArgs(1), + Args: exactArgs(1), RunE: runAgentRestore, } var agentTasksCmd = &cobra.Command{ Use: "tasks ", Short: "List tasks for an agent", - Args: cobra.ExactArgs(1), + Args: exactArgs(1), RunE: runAgentTasks, } @@ -77,14 +77,14 @@ var agentSkillsCmd = &cobra.Command{ var agentSkillsListCmd = &cobra.Command{ Use: "list ", Short: "List skills assigned to an agent", - Args: cobra.ExactArgs(1), + Args: exactArgs(1), RunE: runAgentSkillsList, } var agentSkillsSetCmd = &cobra.Command{ Use: "set ", Short: "Set skills for an agent (replaces all current assignments)", - Args: cobra.ExactArgs(1), + Args: exactArgs(1), RunE: runAgentSkillsSet, } diff --git a/server/cmd/multica/cmd_attachment.go b/server/cmd/multica/cmd_attachment.go index f23f5820..0280d729 100644 --- a/server/cmd/multica/cmd_attachment.go +++ b/server/cmd/multica/cmd_attachment.go @@ -20,8 +20,13 @@ var attachmentCmd = &cobra.Command{ var attachmentDownloadCmd = &cobra.Command{ Use: "download ", Short: "Download an attachment to a local file", - Long: "Fetches the attachment metadata from the API, then downloads the file using its signed URL. Prints the local file path on success.", - Args: cobra.ExactArgs(1), + Long: "Download an attachment by its ID to a local file.", + Example: ` # Download an image attachment to the current directory + $ multica attachment download abc123 + + # Download to a specific directory + $ multica attachment download abc123 -o /tmp/images`, + Args: exactArgs(1), RunE: runAttachmentDownload, } diff --git a/server/cmd/multica/cmd_config.go b/server/cmd/multica/cmd_config.go index 1c9d3a65..532fd511 100644 --- a/server/cmd/multica/cmd_config.go +++ b/server/cmd/multica/cmd_config.go @@ -25,7 +25,7 @@ var configSetCmd = &cobra.Command{ Use: "set ", Short: "Set a CLI configuration value", Long: "Supported keys: server_url, app_url, workspace_id", - Args: cobra.ExactArgs(2), + Args: exactArgs(2), RunE: runConfigSet, } diff --git a/server/cmd/multica/cmd_issue.go b/server/cmd/multica/cmd_issue.go index b3ed8d19..eee480d5 100644 --- a/server/cmd/multica/cmd_issue.go +++ b/server/cmd/multica/cmd_issue.go @@ -28,7 +28,7 @@ var issueListCmd = &cobra.Command{ var issueGetCmd = &cobra.Command{ Use: "get ", Short: "Get issue details", - Args: cobra.ExactArgs(1), + Args: exactArgs(1), RunE: runIssueGet, } @@ -41,21 +41,21 @@ var issueCreateCmd = &cobra.Command{ var issueUpdateCmd = &cobra.Command{ Use: "update ", Short: "Update an issue", - Args: cobra.ExactArgs(1), + Args: exactArgs(1), RunE: runIssueUpdate, } var issueAssignCmd = &cobra.Command{ Use: "assign ", Short: "Assign an issue to a member or agent", - Args: cobra.ExactArgs(1), + Args: exactArgs(1), RunE: runIssueAssign, } var issueStatusCmd = &cobra.Command{ Use: "status ", Short: "Change issue status", - Args: cobra.ExactArgs(2), + Args: exactArgs(2), RunE: runIssueStatus, } @@ -69,21 +69,21 @@ var issueCommentCmd = &cobra.Command{ var issueCommentListCmd = &cobra.Command{ Use: "list ", Short: "List comments on an issue", - Args: cobra.ExactArgs(1), + Args: exactArgs(1), RunE: runIssueCommentList, } var issueCommentAddCmd = &cobra.Command{ Use: "add ", Short: "Add a comment to an issue", - Args: cobra.ExactArgs(1), + Args: exactArgs(1), RunE: runIssueCommentAdd, } var issueCommentDeleteCmd = &cobra.Command{ Use: "delete ", Short: "Delete a comment", - Args: cobra.ExactArgs(1), + Args: exactArgs(1), RunE: runIssueCommentDelete, } @@ -92,14 +92,14 @@ var issueCommentDeleteCmd = &cobra.Command{ var issueRunsCmd = &cobra.Command{ Use: "runs ", Short: "List execution history for an issue", - Args: cobra.ExactArgs(1), + Args: exactArgs(1), RunE: runIssueRuns, } var issueRunMessagesCmd = &cobra.Command{ Use: "run-messages ", Short: "List messages for an execution", - Args: cobra.ExactArgs(1), + Args: exactArgs(1), RunE: runIssueRunMessages, } diff --git a/server/cmd/multica/cmd_repo.go b/server/cmd/multica/cmd_repo.go index 3be0efd9..37cd18f5 100644 --- a/server/cmd/multica/cmd_repo.go +++ b/server/cmd/multica/cmd_repo.go @@ -21,7 +21,7 @@ var repoCheckoutCmd = &cobra.Command{ Use: "checkout ", Short: "Check out a repository into the working directory", Long: "Creates a git worktree from the daemon's bare clone cache. Used by agents to check out repos on demand.", - Args: cobra.ExactArgs(1), + Args: exactArgs(1), RunE: runRepoCheckout, } diff --git a/server/cmd/multica/cmd_runtime.go b/server/cmd/multica/cmd_runtime.go index 12ea8733..04dd8049 100644 --- a/server/cmd/multica/cmd_runtime.go +++ b/server/cmd/multica/cmd_runtime.go @@ -25,28 +25,28 @@ var runtimeListCmd = &cobra.Command{ var runtimeUsageCmd = &cobra.Command{ Use: "usage ", Short: "Get token usage for a runtime", - Args: cobra.ExactArgs(1), + Args: exactArgs(1), RunE: runRuntimeUsage, } var runtimeActivityCmd = &cobra.Command{ Use: "activity ", Short: "Get hourly task activity for a runtime", - Args: cobra.ExactArgs(1), + Args: exactArgs(1), RunE: runRuntimeActivity, } var runtimePingCmd = &cobra.Command{ Use: "ping ", Short: "Ping a runtime to check connectivity", - Args: cobra.ExactArgs(1), + Args: exactArgs(1), RunE: runRuntimePing, } var runtimeUpdateCmd = &cobra.Command{ Use: "update ", Short: "Initiate a CLI update on a runtime", - Args: cobra.ExactArgs(1), + Args: exactArgs(1), RunE: runRuntimeUpdate, } diff --git a/server/cmd/multica/cmd_skill.go b/server/cmd/multica/cmd_skill.go index 5ad0fee8..7734b9eb 100644 --- a/server/cmd/multica/cmd_skill.go +++ b/server/cmd/multica/cmd_skill.go @@ -28,7 +28,7 @@ var skillListCmd = &cobra.Command{ var skillGetCmd = &cobra.Command{ Use: "get ", Short: "Get skill details (includes files)", - Args: cobra.ExactArgs(1), + Args: exactArgs(1), RunE: runSkillGet, } @@ -41,14 +41,14 @@ var skillCreateCmd = &cobra.Command{ var skillUpdateCmd = &cobra.Command{ Use: "update ", Short: "Update a skill", - Args: cobra.ExactArgs(1), + Args: exactArgs(1), RunE: runSkillUpdate, } var skillDeleteCmd = &cobra.Command{ Use: "delete ", Short: "Delete a skill", - Args: cobra.ExactArgs(1), + Args: exactArgs(1), RunE: runSkillDelete, } @@ -68,21 +68,21 @@ var skillFilesCmd = &cobra.Command{ var skillFilesListCmd = &cobra.Command{ Use: "list ", Short: "List files for a skill", - Args: cobra.ExactArgs(1), + Args: exactArgs(1), RunE: runSkillFilesList, } var skillFilesUpsertCmd = &cobra.Command{ Use: "upsert ", Short: "Create or update a skill file", - Args: cobra.ExactArgs(1), + Args: exactArgs(1), RunE: runSkillFilesUpsert, } var skillFilesDeleteCmd = &cobra.Command{ Use: "delete ", Short: "Delete a skill file", - Args: cobra.ExactArgs(2), + Args: exactArgs(2), RunE: runSkillFilesDelete, } diff --git a/server/cmd/multica/cmd_workspace.go b/server/cmd/multica/cmd_workspace.go index 7470280d..44dc37d8 100644 --- a/server/cmd/multica/cmd_workspace.go +++ b/server/cmd/multica/cmd_workspace.go @@ -41,14 +41,14 @@ var workspaceMembersCmd = &cobra.Command{ var workspaceWatchCmd = &cobra.Command{ Use: "watch ", Short: "Add a workspace to the daemon watch list", - Args: cobra.ExactArgs(1), + Args: exactArgs(1), RunE: runWatch, } var workspaceUnwatchCmd = &cobra.Command{ Use: "unwatch ", Short: "Remove a workspace from the daemon watch list", - Args: cobra.ExactArgs(1), + Args: exactArgs(1), RunE: runUnwatch, } diff --git a/server/cmd/multica/help.go b/server/cmd/multica/help.go index 2117db9a..d6b12a62 100644 --- a/server/cmd/multica/help.go +++ b/server/cmd/multica/help.go @@ -15,6 +15,27 @@ const ( groupAdditional = "additional" ) +// errSilent is returned when the error message has already been printed. +var errSilent = fmt.Errorf("") + +// exactArgs returns a cobra.PositionalArgs that validates the arg count +// and prints help on failure, so users see usage context with the error. +func exactArgs(n int) cobra.PositionalArgs { + return func(cmd *cobra.Command, args []string) error { + if len(args) != n { + if n == 1 { + fmt.Fprintf(cmd.ErrOrStderr(), "Error: accepts 1 arg, received %d\n\n", len(args)) + } else { + fmt.Fprintf(cmd.ErrOrStderr(), "Error: accepts %d args, received %d\n\n", n, len(args)) + } + cmd.Help() + return errSilent + } + return nil + } +} + + // initHelp configures the root command to use gh-style help output. func initHelp(root *cobra.Command) { root.SetHelpTemplate(rootHelpTemplate) @@ -120,6 +141,11 @@ COMMANDS {{formatCommandList .Commands}} INHERITED FLAGS --help Show help for command +{{- if .Example}} + +EXAMPLES +{{.Example}} +{{- end}} LEARN MORE Use ` + "`{{.CommandPath}} --help`" + ` for more information about a command. @@ -136,6 +162,11 @@ FLAGS {{- end}} INHERITED FLAGS --help Show help for command +{{- if .Example}} + +EXAMPLES +{{.Example}} +{{- end}} LEARN MORE Use ` + "`multica --help`" + ` for more information about a command. diff --git a/server/cmd/multica/main.go b/server/cmd/multica/main.go index b6de18b6..04d2bdfc 100644 --- a/server/cmd/multica/main.go +++ b/server/cmd/multica/main.go @@ -63,7 +63,9 @@ func init() { func main() { if err := rootCmd.Execute(); err != nil { - fmt.Fprintln(os.Stderr, "Error:", err) + if err != errSilent { + fmt.Fprintln(os.Stderr, "Error:", err) + } os.Exit(1) } }