From 4afef09a03cb14f6cd191c88696f5c6f95aa77cc Mon Sep 17 00:00:00 2001 From: Devv Date: Sat, 4 Apr 2026 15:10:59 -0700 Subject: [PATCH 1/3] refactor(cli): overhaul help output to match gh CLI style - Add gh-style grouped help with CORE/RUNTIME/ADDITIONAL COMMANDS sections - Use UPPERCASE section headers (USAGE, FLAGS, EXAMPLES, LEARN MORE) - Format commands as "name: description" with automatic alignment - Add ENVIRONMENT VARIABLES and EXAMPLES sections to root help - Apply consistent templates to root, subcommand, and leaf commands - Update descriptions from "Manage X" to "Work with X" for gh parity Co-Authored-By: Claude Opus 4.6 (1M context) --- server/cmd/multica/cmd_agent.go | 2 +- server/cmd/multica/cmd_attachment.go | 2 +- server/cmd/multica/cmd_auth.go | 2 +- server/cmd/multica/cmd_config.go | 2 +- server/cmd/multica/cmd_daemon.go | 2 +- server/cmd/multica/cmd_issue.go | 4 +- server/cmd/multica/cmd_repo.go | 2 +- server/cmd/multica/cmd_runtime.go | 2 +- server/cmd/multica/cmd_skill.go | 4 +- server/cmd/multica/cmd_workspace.go | 2 +- server/cmd/multica/help.go | 142 +++++++++++++++++++++++++++ server/cmd/multica/main.go | 39 ++++++-- 12 files changed, 184 insertions(+), 21 deletions(-) create mode 100644 server/cmd/multica/help.go diff --git a/server/cmd/multica/cmd_agent.go b/server/cmd/multica/cmd_agent.go index a6ca6c2a..37c0d1a9 100644 --- a/server/cmd/multica/cmd_agent.go +++ b/server/cmd/multica/cmd_agent.go @@ -17,7 +17,7 @@ import ( var agentCmd = &cobra.Command{ Use: "agent", - Short: "Manage agents", + Short: "Work with agents", } var agentListCmd = &cobra.Command{ diff --git a/server/cmd/multica/cmd_attachment.go b/server/cmd/multica/cmd_attachment.go index 69128685..f23f5820 100644 --- a/server/cmd/multica/cmd_attachment.go +++ b/server/cmd/multica/cmd_attachment.go @@ -14,7 +14,7 @@ import ( var attachmentCmd = &cobra.Command{ Use: "attachment", - Short: "Manage attachments", + Short: "Work with attachments", } var attachmentDownloadCmd = &cobra.Command{ diff --git a/server/cmd/multica/cmd_auth.go b/server/cmd/multica/cmd_auth.go index 0c502cdd..3c6f6c90 100644 --- a/server/cmd/multica/cmd_auth.go +++ b/server/cmd/multica/cmd_auth.go @@ -22,7 +22,7 @@ import ( var authCmd = &cobra.Command{ Use: "auth", - Short: "Manage authentication", + Short: "Authenticate multica with Multica", } var authLoginCmd = &cobra.Command{ diff --git a/server/cmd/multica/cmd_config.go b/server/cmd/multica/cmd_config.go index a6cea730..1c9d3a65 100644 --- a/server/cmd/multica/cmd_config.go +++ b/server/cmd/multica/cmd_config.go @@ -11,7 +11,7 @@ import ( var configCmd = &cobra.Command{ Use: "config", - Short: "Show CLI configuration", + Short: "Manage configuration for multica", RunE: runConfigShow, } diff --git a/server/cmd/multica/cmd_daemon.go b/server/cmd/multica/cmd_daemon.go index fc57703a..38863d97 100644 --- a/server/cmd/multica/cmd_daemon.go +++ b/server/cmd/multica/cmd_daemon.go @@ -23,7 +23,7 @@ import ( var daemonCmd = &cobra.Command{ Use: "daemon", - Short: "Manage the local agent runtime daemon", + Short: "Control the local agent runtime daemon", } var daemonStartCmd = &cobra.Command{ diff --git a/server/cmd/multica/cmd_issue.go b/server/cmd/multica/cmd_issue.go index 322cbf1e..b3ed8d19 100644 --- a/server/cmd/multica/cmd_issue.go +++ b/server/cmd/multica/cmd_issue.go @@ -16,7 +16,7 @@ import ( var issueCmd = &cobra.Command{ Use: "issue", - Short: "Manage issues", + Short: "Work with issues", } var issueListCmd = &cobra.Command{ @@ -63,7 +63,7 @@ var issueStatusCmd = &cobra.Command{ var issueCommentCmd = &cobra.Command{ Use: "comment", - Short: "Manage issue comments", + Short: "Work with issue comments", } var issueCommentListCmd = &cobra.Command{ diff --git a/server/cmd/multica/cmd_repo.go b/server/cmd/multica/cmd_repo.go index e5b692ad..3be0efd9 100644 --- a/server/cmd/multica/cmd_repo.go +++ b/server/cmd/multica/cmd_repo.go @@ -14,7 +14,7 @@ import ( var repoCmd = &cobra.Command{ Use: "repo", - Short: "Manage repositories", + Short: "Work with repositories", } var repoCheckoutCmd = &cobra.Command{ diff --git a/server/cmd/multica/cmd_runtime.go b/server/cmd/multica/cmd_runtime.go index 417efec0..12ea8733 100644 --- a/server/cmd/multica/cmd_runtime.go +++ b/server/cmd/multica/cmd_runtime.go @@ -13,7 +13,7 @@ import ( var runtimeCmd = &cobra.Command{ Use: "runtime", - Short: "Manage agent runtimes", + Short: "Work with agent runtimes", } var runtimeListCmd = &cobra.Command{ diff --git a/server/cmd/multica/cmd_skill.go b/server/cmd/multica/cmd_skill.go index f6de394a..5ad0fee8 100644 --- a/server/cmd/multica/cmd_skill.go +++ b/server/cmd/multica/cmd_skill.go @@ -16,7 +16,7 @@ import ( var skillCmd = &cobra.Command{ Use: "skill", - Short: "Manage skills", + Short: "Work with skills", } var skillListCmd = &cobra.Command{ @@ -62,7 +62,7 @@ var skillImportCmd = &cobra.Command{ var skillFilesCmd = &cobra.Command{ Use: "files", - Short: "Manage skill files", + Short: "Work with skill files", } var skillFilesListCmd = &cobra.Command{ diff --git a/server/cmd/multica/cmd_workspace.go b/server/cmd/multica/cmd_workspace.go index 4461ffa4..7470280d 100644 --- a/server/cmd/multica/cmd_workspace.go +++ b/server/cmd/multica/cmd_workspace.go @@ -15,7 +15,7 @@ import ( var workspaceCmd = &cobra.Command{ Use: "workspace", - Short: "Manage workspaces", + Short: "Work with workspaces", } var workspaceListCmd = &cobra.Command{ diff --git a/server/cmd/multica/help.go b/server/cmd/multica/help.go new file mode 100644 index 00000000..2117db9a --- /dev/null +++ b/server/cmd/multica/help.go @@ -0,0 +1,142 @@ +package main + +import ( + "fmt" + "strings" + "text/template" + + "github.com/spf13/cobra" +) + +// Command group IDs used across the CLI. +const ( + groupCore = "core" + groupRuntime = "runtime" + groupAdditional = "additional" +) + +// initHelp configures the root command to use gh-style help output. +func initHelp(root *cobra.Command) { + root.SetHelpTemplate(rootHelpTemplate) + root.SetUsageTemplate(rootHelpTemplate) + root.CompletionOptions.HiddenDefaultCmd = true + + root.AddGroup( + &cobra.Group{ID: groupCore, Title: "CORE COMMANDS"}, + &cobra.Group{ID: groupRuntime, Title: "RUNTIME COMMANDS"}, + &cobra.Group{ID: groupAdditional, Title: "ADDITIONAL COMMANDS"}, + ) + + // Apply gh-style templates to all commands recursively. + applyTemplates(root) +} + +func applyTemplates(cmd *cobra.Command) { + for _, c := range cmd.Commands() { + if c.HasSubCommands() { + c.SetHelpTemplate(subHelpTemplate) + c.SetUsageTemplate(subHelpTemplate) + } else { + c.SetHelpTemplate(leafHelpTemplate) + c.SetUsageTemplate(leafHelpTemplate) + } + applyTemplates(c) + } +} + +// formatCommandList formats a list of commands in "name: description" style +// with automatic alignment, matching gh's output. +func formatCommandList(cmds []*cobra.Command) string { + if len(cmds) == 0 { + return "" + } + + maxLen := 0 + for _, c := range cmds { + if c.IsAvailableCommand() && len(c.Name()) > maxLen { + maxLen = len(c.Name()) + } + } + + var b strings.Builder + for _, c := range cmds { + if !c.IsAvailableCommand() { + continue + } + padding := strings.Repeat(" ", maxLen-len(c.Name())) + fmt.Fprintf(&b, " %s:%s %s\n", c.Name(), padding, c.Short) + } + return b.String() +} + +// commandsInGroup returns commands that belong to a specific group. +func commandsInGroup(cmds []*cobra.Command, groupID string) []*cobra.Command { + var result []*cobra.Command + for _, c := range cmds { + if c.GroupID == groupID && c.IsAvailableCommand() { + result = append(result, c) + } + } + return result +} + +func init() { + cobra.AddTemplateFuncs(template.FuncMap{ + "formatCommandList": formatCommandList, + "commandsInGroup": commandsInGroup, + }) +} + +var rootHelpTemplate = `Work seamlessly with Multica from the command line. + +USAGE + multica [flags] +{{range .Groups}} +{{.Title}} +{{formatCommandList (commandsInGroup $.Commands .ID)}} +{{- end}} +FLAGS +{{.LocalFlags.FlagUsages}} +EXAMPLES + $ multica login + $ multica issue list --output json + $ multica daemon start + $ multica agent list --output json + +ENVIRONMENT VARIABLES + MULTICA_SERVER_URL Override the default server URL + MULTICA_WORKSPACE_ID Set the active workspace + +LEARN MORE + Use ` + "`multica --help`" + ` for more information about a command. +` + +var subHelpTemplate = `{{.Short}} + +USAGE + {{.CommandPath}} [flags] + +COMMANDS +{{formatCommandList .Commands}} +INHERITED FLAGS + --help Show help for command + +LEARN MORE + Use ` + "`{{.CommandPath}} --help`" + ` for more information about a command. +` + +var leafHelpTemplate = `{{if .Long}}{{.Long}}{{else}}{{.Short}}{{end}} + +USAGE + {{.UseLine}} +{{- if .HasLocalFlags}} + +FLAGS +{{.LocalFlags.FlagUsages}} +{{- end}} +INHERITED FLAGS + --help Show help for command + +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 53cc903f..b6de18b6 100644 --- a/server/cmd/multica/main.go +++ b/server/cmd/multica/main.go @@ -15,7 +15,7 @@ var ( var rootCmd = &cobra.Command{ Use: "multica", Short: "Multica CLI — local agent runtime and management tool", - Long: "multica manages local agent runtimes and provides control commands for the Multica platform.", + Long: "Work seamlessly with Multica from the command line.", SilenceUsage: true, SilenceErrors: true, } @@ -25,19 +25,40 @@ func init() { rootCmd.PersistentFlags().String("workspace-id", "", "Workspace ID (env: MULTICA_WORKSPACE_ID)") rootCmd.PersistentFlags().String("profile", "", "Configuration profile name (e.g. dev) — isolates config, daemon state, and workspaces") - rootCmd.AddCommand(loginCmd) - rootCmd.AddCommand(authCmd) - rootCmd.AddCommand(daemonCmd) + // Core commands + issueCmd.GroupID = groupCore + agentCmd.GroupID = groupCore + workspaceCmd.GroupID = groupCore + repoCmd.GroupID = groupCore + skillCmd.GroupID = groupCore + + // Runtime commands + daemonCmd.GroupID = groupRuntime + runtimeCmd.GroupID = groupRuntime + + // Additional commands + authCmd.GroupID = groupAdditional + loginCmd.GroupID = groupAdditional + attachmentCmd.GroupID = groupAdditional + configCmd.GroupID = groupAdditional + updateCmd.GroupID = groupAdditional + versionCmd.GroupID = groupAdditional + + rootCmd.AddCommand(issueCmd) rootCmd.AddCommand(agentCmd) rootCmd.AddCommand(workspaceCmd) - rootCmd.AddCommand(configCmd) - rootCmd.AddCommand(issueCmd) - rootCmd.AddCommand(attachmentCmd) rootCmd.AddCommand(repoCmd) - rootCmd.AddCommand(versionCmd) - rootCmd.AddCommand(updateCmd) rootCmd.AddCommand(skillCmd) + rootCmd.AddCommand(daemonCmd) rootCmd.AddCommand(runtimeCmd) + rootCmd.AddCommand(authCmd) + rootCmd.AddCommand(loginCmd) + rootCmd.AddCommand(attachmentCmd) + rootCmd.AddCommand(configCmd) + rootCmd.AddCommand(updateCmd) + rootCmd.AddCommand(versionCmd) + + initHelp(rootCmd) } func main() { From d6a5ba4d5e14b04e3856439746ed2d9c7405273e Mon Sep 17 00:00:00 2001 From: Devv Date: Sat, 4 Apr 2026 15:25:14 -0700 Subject: [PATCH 2/3] fix(execenv): add explicit instruction for agents to always use multica CLI Agents were using curl/wget to access Multica attachment URLs directly, which fails due to authentication. Add a prominent "Important" section to the generated CLAUDE.md template that explicitly prohibits direct HTTP access and instructs agents to escalate missing CLI functionality to their workspace owner. --- server/internal/daemon/execenv/runtime_config.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/server/internal/daemon/execenv/runtime_config.go b/server/internal/daemon/execenv/runtime_config.go index 479a18cf..e9e8f9ce 100644 --- a/server/internal/daemon/execenv/runtime_config.go +++ b/server/internal/daemon/execenv/runtime_config.go @@ -143,6 +143,13 @@ func buildMetaSkillContent(provider string, ctx TaskContextForEnv) string { b.WriteString("This downloads the file to the current directory and prints the local path. Use `-o ` to save elsewhere.\n") b.WriteString("After downloading, you can read the file directly (e.g. view an image, read a document).\n\n") + b.WriteString("## Important: Always Use the `multica` CLI\n\n") + b.WriteString("All interactions with Multica platform resources — including issues, comments, attachments, images, files, and any other platform data — **must** go through the `multica` CLI. ") + b.WriteString("Do NOT use `curl`, `wget`, or any other HTTP client to access Multica URLs or APIs directly. ") + b.WriteString("Multica resource URLs require authenticated access that only the `multica` CLI can provide.\n\n") + b.WriteString("If you need to perform an operation that is not covered by any existing `multica` command, ") + b.WriteString("do NOT attempt to work around it. Instead, post a comment mentioning the workspace owner to request the missing functionality.\n\n") + b.WriteString("## Output\n\n") b.WriteString("Keep comments concise and natural — state the outcome, not the process.\n") b.WriteString("Good: \"Fixed the login redirect. PR: https://...\"\n") From 4c0dbbf1c80dedcd8008c8b728977b2c137a219a Mon Sep 17 00:00:00 2001 From: yushen Date: Sun, 5 Apr 2026 07:00:19 +0800 Subject: [PATCH 3/3] =?UTF-8?q?refactor(cli):=20improve=20help=20UX=20?= =?UTF-8?q?=E2=80=94=20add=20examples=20support,=20show=20help=20on=20arg?= =?UTF-8?q?=20errors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add EXAMPLES section to leaf and sub help templates (gh CLI style) - Add example to attachment download command - Simplify attachment download description - Show help output when required args are missing (error first, then help) - Replace cobra.ExactArgs with custom exactArgs that prints help on failure Co-Authored-By: Claude Opus 4.6 (1M context) --- server/cmd/multica/cmd_agent.go | 14 ++++++------- server/cmd/multica/cmd_attachment.go | 9 +++++++-- server/cmd/multica/cmd_config.go | 2 +- server/cmd/multica/cmd_issue.go | 18 ++++++++--------- server/cmd/multica/cmd_repo.go | 2 +- server/cmd/multica/cmd_runtime.go | 8 ++++---- server/cmd/multica/cmd_skill.go | 12 +++++------ server/cmd/multica/cmd_workspace.go | 4 ++-- server/cmd/multica/help.go | 30 ++++++++++++++++++++++++++++ server/cmd/multica/main.go | 4 +++- 10 files changed, 70 insertions(+), 33 deletions(-) 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..63190edf 100644 --- a/server/cmd/multica/help.go +++ b/server/cmd/multica/help.go @@ -15,6 +15,26 @@ 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 +140,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 +161,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) } }