refactor(cli): improve help UX — add examples support, show help on arg errors
- 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) <noreply@anthropic.com>
This commit is contained in:
parent
d6a5ba4d5e
commit
4c0dbbf1c8
10 changed files with 70 additions and 33 deletions
|
|
@ -29,7 +29,7 @@ var agentListCmd = &cobra.Command{
|
||||||
var agentGetCmd = &cobra.Command{
|
var agentGetCmd = &cobra.Command{
|
||||||
Use: "get <id>",
|
Use: "get <id>",
|
||||||
Short: "Get agent details",
|
Short: "Get agent details",
|
||||||
Args: cobra.ExactArgs(1),
|
Args: exactArgs(1),
|
||||||
RunE: runAgentGet,
|
RunE: runAgentGet,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -42,28 +42,28 @@ var agentCreateCmd = &cobra.Command{
|
||||||
var agentUpdateCmd = &cobra.Command{
|
var agentUpdateCmd = &cobra.Command{
|
||||||
Use: "update <id>",
|
Use: "update <id>",
|
||||||
Short: "Update an agent",
|
Short: "Update an agent",
|
||||||
Args: cobra.ExactArgs(1),
|
Args: exactArgs(1),
|
||||||
RunE: runAgentUpdate,
|
RunE: runAgentUpdate,
|
||||||
}
|
}
|
||||||
|
|
||||||
var agentArchiveCmd = &cobra.Command{
|
var agentArchiveCmd = &cobra.Command{
|
||||||
Use: "archive <id>",
|
Use: "archive <id>",
|
||||||
Short: "Archive an agent",
|
Short: "Archive an agent",
|
||||||
Args: cobra.ExactArgs(1),
|
Args: exactArgs(1),
|
||||||
RunE: runAgentArchive,
|
RunE: runAgentArchive,
|
||||||
}
|
}
|
||||||
|
|
||||||
var agentRestoreCmd = &cobra.Command{
|
var agentRestoreCmd = &cobra.Command{
|
||||||
Use: "restore <id>",
|
Use: "restore <id>",
|
||||||
Short: "Restore an archived agent",
|
Short: "Restore an archived agent",
|
||||||
Args: cobra.ExactArgs(1),
|
Args: exactArgs(1),
|
||||||
RunE: runAgentRestore,
|
RunE: runAgentRestore,
|
||||||
}
|
}
|
||||||
|
|
||||||
var agentTasksCmd = &cobra.Command{
|
var agentTasksCmd = &cobra.Command{
|
||||||
Use: "tasks <id>",
|
Use: "tasks <id>",
|
||||||
Short: "List tasks for an agent",
|
Short: "List tasks for an agent",
|
||||||
Args: cobra.ExactArgs(1),
|
Args: exactArgs(1),
|
||||||
RunE: runAgentTasks,
|
RunE: runAgentTasks,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -77,14 +77,14 @@ var agentSkillsCmd = &cobra.Command{
|
||||||
var agentSkillsListCmd = &cobra.Command{
|
var agentSkillsListCmd = &cobra.Command{
|
||||||
Use: "list <agent-id>",
|
Use: "list <agent-id>",
|
||||||
Short: "List skills assigned to an agent",
|
Short: "List skills assigned to an agent",
|
||||||
Args: cobra.ExactArgs(1),
|
Args: exactArgs(1),
|
||||||
RunE: runAgentSkillsList,
|
RunE: runAgentSkillsList,
|
||||||
}
|
}
|
||||||
|
|
||||||
var agentSkillsSetCmd = &cobra.Command{
|
var agentSkillsSetCmd = &cobra.Command{
|
||||||
Use: "set <agent-id>",
|
Use: "set <agent-id>",
|
||||||
Short: "Set skills for an agent (replaces all current assignments)",
|
Short: "Set skills for an agent (replaces all current assignments)",
|
||||||
Args: cobra.ExactArgs(1),
|
Args: exactArgs(1),
|
||||||
RunE: runAgentSkillsSet,
|
RunE: runAgentSkillsSet,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,13 @@ var attachmentCmd = &cobra.Command{
|
||||||
var attachmentDownloadCmd = &cobra.Command{
|
var attachmentDownloadCmd = &cobra.Command{
|
||||||
Use: "download <attachment-id>",
|
Use: "download <attachment-id>",
|
||||||
Short: "Download an attachment to a local file",
|
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.",
|
Long: "Download an attachment by its ID to a local file.",
|
||||||
Args: cobra.ExactArgs(1),
|
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,
|
RunE: runAttachmentDownload,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ var configSetCmd = &cobra.Command{
|
||||||
Use: "set <key> <value>",
|
Use: "set <key> <value>",
|
||||||
Short: "Set a CLI configuration value",
|
Short: "Set a CLI configuration value",
|
||||||
Long: "Supported keys: server_url, app_url, workspace_id",
|
Long: "Supported keys: server_url, app_url, workspace_id",
|
||||||
Args: cobra.ExactArgs(2),
|
Args: exactArgs(2),
|
||||||
RunE: runConfigSet,
|
RunE: runConfigSet,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ var issueListCmd = &cobra.Command{
|
||||||
var issueGetCmd = &cobra.Command{
|
var issueGetCmd = &cobra.Command{
|
||||||
Use: "get <id>",
|
Use: "get <id>",
|
||||||
Short: "Get issue details",
|
Short: "Get issue details",
|
||||||
Args: cobra.ExactArgs(1),
|
Args: exactArgs(1),
|
||||||
RunE: runIssueGet,
|
RunE: runIssueGet,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -41,21 +41,21 @@ var issueCreateCmd = &cobra.Command{
|
||||||
var issueUpdateCmd = &cobra.Command{
|
var issueUpdateCmd = &cobra.Command{
|
||||||
Use: "update <id>",
|
Use: "update <id>",
|
||||||
Short: "Update an issue",
|
Short: "Update an issue",
|
||||||
Args: cobra.ExactArgs(1),
|
Args: exactArgs(1),
|
||||||
RunE: runIssueUpdate,
|
RunE: runIssueUpdate,
|
||||||
}
|
}
|
||||||
|
|
||||||
var issueAssignCmd = &cobra.Command{
|
var issueAssignCmd = &cobra.Command{
|
||||||
Use: "assign <id>",
|
Use: "assign <id>",
|
||||||
Short: "Assign an issue to a member or agent",
|
Short: "Assign an issue to a member or agent",
|
||||||
Args: cobra.ExactArgs(1),
|
Args: exactArgs(1),
|
||||||
RunE: runIssueAssign,
|
RunE: runIssueAssign,
|
||||||
}
|
}
|
||||||
|
|
||||||
var issueStatusCmd = &cobra.Command{
|
var issueStatusCmd = &cobra.Command{
|
||||||
Use: "status <id> <status>",
|
Use: "status <id> <status>",
|
||||||
Short: "Change issue status",
|
Short: "Change issue status",
|
||||||
Args: cobra.ExactArgs(2),
|
Args: exactArgs(2),
|
||||||
RunE: runIssueStatus,
|
RunE: runIssueStatus,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -69,21 +69,21 @@ var issueCommentCmd = &cobra.Command{
|
||||||
var issueCommentListCmd = &cobra.Command{
|
var issueCommentListCmd = &cobra.Command{
|
||||||
Use: "list <issue-id>",
|
Use: "list <issue-id>",
|
||||||
Short: "List comments on an issue",
|
Short: "List comments on an issue",
|
||||||
Args: cobra.ExactArgs(1),
|
Args: exactArgs(1),
|
||||||
RunE: runIssueCommentList,
|
RunE: runIssueCommentList,
|
||||||
}
|
}
|
||||||
|
|
||||||
var issueCommentAddCmd = &cobra.Command{
|
var issueCommentAddCmd = &cobra.Command{
|
||||||
Use: "add <issue-id>",
|
Use: "add <issue-id>",
|
||||||
Short: "Add a comment to an issue",
|
Short: "Add a comment to an issue",
|
||||||
Args: cobra.ExactArgs(1),
|
Args: exactArgs(1),
|
||||||
RunE: runIssueCommentAdd,
|
RunE: runIssueCommentAdd,
|
||||||
}
|
}
|
||||||
|
|
||||||
var issueCommentDeleteCmd = &cobra.Command{
|
var issueCommentDeleteCmd = &cobra.Command{
|
||||||
Use: "delete <comment-id>",
|
Use: "delete <comment-id>",
|
||||||
Short: "Delete a comment",
|
Short: "Delete a comment",
|
||||||
Args: cobra.ExactArgs(1),
|
Args: exactArgs(1),
|
||||||
RunE: runIssueCommentDelete,
|
RunE: runIssueCommentDelete,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -92,14 +92,14 @@ var issueCommentDeleteCmd = &cobra.Command{
|
||||||
var issueRunsCmd = &cobra.Command{
|
var issueRunsCmd = &cobra.Command{
|
||||||
Use: "runs <issue-id>",
|
Use: "runs <issue-id>",
|
||||||
Short: "List execution history for an issue",
|
Short: "List execution history for an issue",
|
||||||
Args: cobra.ExactArgs(1),
|
Args: exactArgs(1),
|
||||||
RunE: runIssueRuns,
|
RunE: runIssueRuns,
|
||||||
}
|
}
|
||||||
|
|
||||||
var issueRunMessagesCmd = &cobra.Command{
|
var issueRunMessagesCmd = &cobra.Command{
|
||||||
Use: "run-messages <task-id>",
|
Use: "run-messages <task-id>",
|
||||||
Short: "List messages for an execution",
|
Short: "List messages for an execution",
|
||||||
Args: cobra.ExactArgs(1),
|
Args: exactArgs(1),
|
||||||
RunE: runIssueRunMessages,
|
RunE: runIssueRunMessages,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ var repoCheckoutCmd = &cobra.Command{
|
||||||
Use: "checkout <url>",
|
Use: "checkout <url>",
|
||||||
Short: "Check out a repository into the working directory",
|
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.",
|
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,
|
RunE: runRepoCheckout,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,28 +25,28 @@ var runtimeListCmd = &cobra.Command{
|
||||||
var runtimeUsageCmd = &cobra.Command{
|
var runtimeUsageCmd = &cobra.Command{
|
||||||
Use: "usage <runtime-id>",
|
Use: "usage <runtime-id>",
|
||||||
Short: "Get token usage for a runtime",
|
Short: "Get token usage for a runtime",
|
||||||
Args: cobra.ExactArgs(1),
|
Args: exactArgs(1),
|
||||||
RunE: runRuntimeUsage,
|
RunE: runRuntimeUsage,
|
||||||
}
|
}
|
||||||
|
|
||||||
var runtimeActivityCmd = &cobra.Command{
|
var runtimeActivityCmd = &cobra.Command{
|
||||||
Use: "activity <runtime-id>",
|
Use: "activity <runtime-id>",
|
||||||
Short: "Get hourly task activity for a runtime",
|
Short: "Get hourly task activity for a runtime",
|
||||||
Args: cobra.ExactArgs(1),
|
Args: exactArgs(1),
|
||||||
RunE: runRuntimeActivity,
|
RunE: runRuntimeActivity,
|
||||||
}
|
}
|
||||||
|
|
||||||
var runtimePingCmd = &cobra.Command{
|
var runtimePingCmd = &cobra.Command{
|
||||||
Use: "ping <runtime-id>",
|
Use: "ping <runtime-id>",
|
||||||
Short: "Ping a runtime to check connectivity",
|
Short: "Ping a runtime to check connectivity",
|
||||||
Args: cobra.ExactArgs(1),
|
Args: exactArgs(1),
|
||||||
RunE: runRuntimePing,
|
RunE: runRuntimePing,
|
||||||
}
|
}
|
||||||
|
|
||||||
var runtimeUpdateCmd = &cobra.Command{
|
var runtimeUpdateCmd = &cobra.Command{
|
||||||
Use: "update <runtime-id>",
|
Use: "update <runtime-id>",
|
||||||
Short: "Initiate a CLI update on a runtime",
|
Short: "Initiate a CLI update on a runtime",
|
||||||
Args: cobra.ExactArgs(1),
|
Args: exactArgs(1),
|
||||||
RunE: runRuntimeUpdate,
|
RunE: runRuntimeUpdate,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ var skillListCmd = &cobra.Command{
|
||||||
var skillGetCmd = &cobra.Command{
|
var skillGetCmd = &cobra.Command{
|
||||||
Use: "get <id>",
|
Use: "get <id>",
|
||||||
Short: "Get skill details (includes files)",
|
Short: "Get skill details (includes files)",
|
||||||
Args: cobra.ExactArgs(1),
|
Args: exactArgs(1),
|
||||||
RunE: runSkillGet,
|
RunE: runSkillGet,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -41,14 +41,14 @@ var skillCreateCmd = &cobra.Command{
|
||||||
var skillUpdateCmd = &cobra.Command{
|
var skillUpdateCmd = &cobra.Command{
|
||||||
Use: "update <id>",
|
Use: "update <id>",
|
||||||
Short: "Update a skill",
|
Short: "Update a skill",
|
||||||
Args: cobra.ExactArgs(1),
|
Args: exactArgs(1),
|
||||||
RunE: runSkillUpdate,
|
RunE: runSkillUpdate,
|
||||||
}
|
}
|
||||||
|
|
||||||
var skillDeleteCmd = &cobra.Command{
|
var skillDeleteCmd = &cobra.Command{
|
||||||
Use: "delete <id>",
|
Use: "delete <id>",
|
||||||
Short: "Delete a skill",
|
Short: "Delete a skill",
|
||||||
Args: cobra.ExactArgs(1),
|
Args: exactArgs(1),
|
||||||
RunE: runSkillDelete,
|
RunE: runSkillDelete,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -68,21 +68,21 @@ var skillFilesCmd = &cobra.Command{
|
||||||
var skillFilesListCmd = &cobra.Command{
|
var skillFilesListCmd = &cobra.Command{
|
||||||
Use: "list <skill-id>",
|
Use: "list <skill-id>",
|
||||||
Short: "List files for a skill",
|
Short: "List files for a skill",
|
||||||
Args: cobra.ExactArgs(1),
|
Args: exactArgs(1),
|
||||||
RunE: runSkillFilesList,
|
RunE: runSkillFilesList,
|
||||||
}
|
}
|
||||||
|
|
||||||
var skillFilesUpsertCmd = &cobra.Command{
|
var skillFilesUpsertCmd = &cobra.Command{
|
||||||
Use: "upsert <skill-id>",
|
Use: "upsert <skill-id>",
|
||||||
Short: "Create or update a skill file",
|
Short: "Create or update a skill file",
|
||||||
Args: cobra.ExactArgs(1),
|
Args: exactArgs(1),
|
||||||
RunE: runSkillFilesUpsert,
|
RunE: runSkillFilesUpsert,
|
||||||
}
|
}
|
||||||
|
|
||||||
var skillFilesDeleteCmd = &cobra.Command{
|
var skillFilesDeleteCmd = &cobra.Command{
|
||||||
Use: "delete <skill-id> <file-id>",
|
Use: "delete <skill-id> <file-id>",
|
||||||
Short: "Delete a skill file",
|
Short: "Delete a skill file",
|
||||||
Args: cobra.ExactArgs(2),
|
Args: exactArgs(2),
|
||||||
RunE: runSkillFilesDelete,
|
RunE: runSkillFilesDelete,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,14 +41,14 @@ var workspaceMembersCmd = &cobra.Command{
|
||||||
var workspaceWatchCmd = &cobra.Command{
|
var workspaceWatchCmd = &cobra.Command{
|
||||||
Use: "watch <workspace-id>",
|
Use: "watch <workspace-id>",
|
||||||
Short: "Add a workspace to the daemon watch list",
|
Short: "Add a workspace to the daemon watch list",
|
||||||
Args: cobra.ExactArgs(1),
|
Args: exactArgs(1),
|
||||||
RunE: runWatch,
|
RunE: runWatch,
|
||||||
}
|
}
|
||||||
|
|
||||||
var workspaceUnwatchCmd = &cobra.Command{
|
var workspaceUnwatchCmd = &cobra.Command{
|
||||||
Use: "unwatch <workspace-id>",
|
Use: "unwatch <workspace-id>",
|
||||||
Short: "Remove a workspace from the daemon watch list",
|
Short: "Remove a workspace from the daemon watch list",
|
||||||
Args: cobra.ExactArgs(1),
|
Args: exactArgs(1),
|
||||||
RunE: runUnwatch,
|
RunE: runUnwatch,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,26 @@ const (
|
||||||
groupAdditional = "additional"
|
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.
|
// initHelp configures the root command to use gh-style help output.
|
||||||
func initHelp(root *cobra.Command) {
|
func initHelp(root *cobra.Command) {
|
||||||
root.SetHelpTemplate(rootHelpTemplate)
|
root.SetHelpTemplate(rootHelpTemplate)
|
||||||
|
|
@ -120,6 +140,11 @@ COMMANDS
|
||||||
{{formatCommandList .Commands}}
|
{{formatCommandList .Commands}}
|
||||||
INHERITED FLAGS
|
INHERITED FLAGS
|
||||||
--help Show help for command
|
--help Show help for command
|
||||||
|
{{- if .Example}}
|
||||||
|
|
||||||
|
EXAMPLES
|
||||||
|
{{.Example}}
|
||||||
|
{{- end}}
|
||||||
|
|
||||||
LEARN MORE
|
LEARN MORE
|
||||||
Use ` + "`{{.CommandPath}} <command> --help`" + ` for more information about a command.
|
Use ` + "`{{.CommandPath}} <command> --help`" + ` for more information about a command.
|
||||||
|
|
@ -136,6 +161,11 @@ FLAGS
|
||||||
{{- end}}
|
{{- end}}
|
||||||
INHERITED FLAGS
|
INHERITED FLAGS
|
||||||
--help Show help for command
|
--help Show help for command
|
||||||
|
{{- if .Example}}
|
||||||
|
|
||||||
|
EXAMPLES
|
||||||
|
{{.Example}}
|
||||||
|
{{- end}}
|
||||||
|
|
||||||
LEARN MORE
|
LEARN MORE
|
||||||
Use ` + "`multica <command> <subcommand> --help`" + ` for more information about a command.
|
Use ` + "`multica <command> <subcommand> --help`" + ` for more information about a command.
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,9 @@ func init() {
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if err := rootCmd.Execute(); err != nil {
|
if err := rootCmd.Execute(); err != nil {
|
||||||
fmt.Fprintln(os.Stderr, "Error:", err)
|
if err != errSilent {
|
||||||
|
fmt.Fprintln(os.Stderr, "Error:", err)
|
||||||
|
}
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue